summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r0drv
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/r0drv
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/r0drv')
-rw-r--r--src/VBox/Runtime/r0drv/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/RTR0DbgKrnlInfoGetSymbol.cpp56
-rw-r--r--src/VBox/Runtime/r0drv/alloc-ef-r0drv.cpp1019
-rw-r--r--src/VBox/Runtime/r0drv/alloc-r0drv.cpp421
-rw-r--r--src/VBox/Runtime/r0drv/alloc-r0drv.h111
-rw-r--r--src/VBox/Runtime/r0drv/darwin/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/darwin/RTLogWriteDebugger-r0drv-darwin.cpp52
-rw-r--r--src/VBox/Runtime/r0drv/darwin/RTLogWriteStdOut-r0drv-darwin.cpp52
-rw-r--r--src/VBox/Runtime/r0drv/darwin/alloc-r0drv-darwin.cpp152
-rw-r--r--src/VBox/Runtime/r0drv/darwin/assert-r0drv-darwin.cpp87
-rw-r--r--src/VBox/Runtime/r0drv/darwin/dbgkrnlinfo-r0drv-darwin.cpp1576
-rw-r--r--src/VBox/Runtime/r0drv/darwin/fileio-r0drv-darwin.cpp323
-rw-r--r--src/VBox/Runtime/r0drv/darwin/initterm-r0drv-darwin.cpp133
-rw-r--r--src/VBox/Runtime/r0drv/darwin/memobj-r0drv-darwin.cpp1571
-rw-r--r--src/VBox/Runtime/r0drv/darwin/memuserkernel-r0drv-darwin.cpp128
-rw-r--r--src/VBox/Runtime/r0drv/darwin/mp-r0drv-darwin.cpp324
-rw-r--r--src/VBox/Runtime/r0drv/darwin/process-r0drv-darwin.cpp56
-rw-r--r--src/VBox/Runtime/r0drv/darwin/rtStrFormatKernelAddress-r0drv-darwin.cpp60
-rw-r--r--src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp445
-rw-r--r--src/VBox/Runtime/r0drv/darwin/semeventmulti-r0drv-darwin.cpp467
-rw-r--r--src/VBox/Runtime/r0drv/darwin/semfastmutex-r0drv-darwin.cpp150
-rw-r--r--src/VBox/Runtime/r0drv/darwin/semmutex-r0drv-darwin.cpp417
-rw-r--r--src/VBox/Runtime/r0drv/darwin/spinlock-r0drv-darwin.cpp187
-rw-r--r--src/VBox/Runtime/r0drv/darwin/the-darwin-kernel.h262
-rw-r--r--src/VBox/Runtime/r0drv/darwin/thread-r0drv-darwin.cpp92
-rw-r--r--src/VBox/Runtime/r0drv/darwin/thread2-r0drv-darwin.cpp202
-rw-r--r--src/VBox/Runtime/r0drv/darwin/threadpreempt-r0drv-darwin.cpp213
-rw-r--r--src/VBox/Runtime/r0drv/darwin/time-r0drv-darwin.cpp108
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c153
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/assert-r0drv-freebsd.c104
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c87
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c906
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c93
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c322
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c85
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c297
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c362
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c149
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c229
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h344
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c244
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h170
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c204
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c189
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c108
-rw-r--r--src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c320
-rw-r--r--src/VBox/Runtime/r0drv/generic/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTMpIsCpuWorkPending-r0drv-generic.cpp55
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTMpOn-r0drv-generic.cpp104
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTMpPokeCpu-r0drv-generic.cpp58
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTThreadPreemptDisable-r0drv-generic.cpp54
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsEnabled-r0drv-generic.cpp53
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPending-r0drv-generic.cpp53
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPendingTrusty-r0drv-generic.cpp51
-rw-r--r--src/VBox/Runtime/r0drv/generic/RTThreadPreemptRestore-r0drv-generic.cpp54
-rw-r--r--src/VBox/Runtime/r0drv/generic/mpnotification-r0drv-generic.cpp75
-rw-r--r--src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c513
-rw-r--r--src/VBox/Runtime/r0drv/generic/threadctxhooks-r0drv-generic.cpp83
-rw-r--r--src/VBox/Runtime/r0drv/haiku/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/haiku/RTLogWriteDebugger-r0drv-haiku.c52
-rw-r--r--src/VBox/Runtime/r0drv/haiku/RTLogWriteStdOut-r0drv-haiku.c51
-rw-r--r--src/VBox/Runtime/r0drv/haiku/alloc-r0drv-haiku.c134
-rw-r--r--src/VBox/Runtime/r0drv/haiku/assert-r0drv-haiku.c78
-rw-r--r--src/VBox/Runtime/r0drv/haiku/initterm-r0drv-haiku.c58
-rw-r--r--src/VBox/Runtime/r0drv/haiku/memobj-r0drv-haiku.c686
-rw-r--r--src/VBox/Runtime/r0drv/haiku/mp-r0drv-haiku.c246
-rw-r--r--src/VBox/Runtime/r0drv/haiku/process-r0drv-haiku.c56
-rw-r--r--src/VBox/Runtime/r0drv/haiku/semevent-r0drv-haiku.c282
-rw-r--r--src/VBox/Runtime/r0drv/haiku/semeventmulti-r0drv-haiku.c310
-rw-r--r--src/VBox/Runtime/r0drv/haiku/semfastmutex-r0drv-haiku.c130
-rw-r--r--src/VBox/Runtime/r0drv/haiku/semmutex-r0drv-haiku.c243
-rw-r--r--src/VBox/Runtime/r0drv/haiku/spinlock-r0drv-haiku.c148
-rw-r--r--src/VBox/Runtime/r0drv/haiku/the-haiku-kernel.h126
-rw-r--r--src/VBox/Runtime/r0drv/haiku/thread-r0drv-haiku.c137
-rw-r--r--src/VBox/Runtime/r0drv/haiku/thread2-r0drv-haiku.c148
-rw-r--r--src/VBox/Runtime/r0drv/haiku/time-r0drv-haiku.c89
-rw-r--r--src/VBox/Runtime/r0drv/initterm-r0drv.cpp174
-rw-r--r--src/VBox/Runtime/r0drv/linux/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c53
-rw-r--r--src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c257
-rw-r--r--src/VBox/Runtime/r0drv/linux/assert-r0drv-linux.c84
-rw-r--r--src/VBox/Runtime/r0drv/linux/initterm-r0drv-linux.c138
-rw-r--r--src/VBox/Runtime/r0drv/linux/memobj-r0drv-linux.c2104
-rw-r--r--src/VBox/Runtime/r0drv/linux/memuserkernel-r0drv-linux.c191
-rw-r--r--src/VBox/Runtime/r0drv/linux/mp-r0drv-linux.c640
-rw-r--r--src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c258
-rw-r--r--src/VBox/Runtime/r0drv/linux/process-r0drv-linux.c59
-rw-r--r--src/VBox/Runtime/r0drv/linux/rtStrFormatKernelAddress-r0drv-linux.c66
-rw-r--r--src/VBox/Runtime/r0drv/linux/semevent-r0drv-linux.c296
-rw-r--r--src/VBox/Runtime/r0drv/linux/semeventmulti-r0drv-linux.c361
-rw-r--r--src/VBox/Runtime/r0drv/linux/semfastmutex-r0drv-linux.c167
-rw-r--r--src/VBox/Runtime/r0drv/linux/semmutex-r0drv-linux.c431
-rw-r--r--src/VBox/Runtime/r0drv/linux/spinlock-r0drv-linux.c196
-rw-r--r--src/VBox/Runtime/r0drv/linux/string.h70
-rw-r--r--src/VBox/Runtime/r0drv/linux/the-linux-kernel.h494
-rw-r--r--src/VBox/Runtime/r0drv/linux/thread-r0drv-linux.c285
-rw-r--r--src/VBox/Runtime/r0drv/linux/thread2-r0drv-linux.c243
-rw-r--r--src/VBox/Runtime/r0drv/linux/threadctxhooks-r0drv-linux.c341
-rw-r--r--src/VBox/Runtime/r0drv/linux/time-r0drv-linux.c221
-rw-r--r--src/VBox/Runtime/r0drv/linux/timer-r0drv-linux.c1739
-rw-r--r--src/VBox/Runtime/r0drv/linux/waitqueue-r0drv-linux.h302
-rw-r--r--src/VBox/Runtime/r0drv/memobj-r0drv.cpp824
-rw-r--r--src/VBox/Runtime/r0drv/mp-r0drv.h95
-rw-r--r--src/VBox/Runtime/r0drv/mpnotification-r0drv.c332
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/RTLogWriteStdOut-r0drv-netbsd.c49
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/alloc-r0drv-netbsd.c176
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/assert-r0drv-netbsd.c95
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/initterm-r0drv-netbsd.c84
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/memobj-r0drv-netbsd.c586
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/memuserkernel-r0drv-netbsd.c92
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/mp-r0drv-netbsd.c53
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/process-r0drv-netbsd.c83
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/semevent-r0drv-netbsd.c294
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/semeventmulti-r0drv-netbsd.c360
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/semfastmutex-r0drv-netbsd.c146
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/semmutex-r0drv-netbsd.c228
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/sleepqueue-r0drv-netbsd.h300
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/spinlock-r0drv-netbsd.c180
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/the-netbsd-kernel.h107
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/thread-r0drv-netbsd.c193
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/thread2-r0drv-netbsd.c167
-rw-r--r--src/VBox/Runtime/r0drv/netbsd/time-r0drv-netbsd.c105
-rw-r--r--src/VBox/Runtime/r0drv/nt/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp49
-rw-r--r--src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp71
-rw-r--r--src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp145
-rw-r--r--src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm69
-rw-r--r--src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp76
-rw-r--r--src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp918
-rw-r--r--src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp534
-rw-r--r--src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h207
-rw-r--r--src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp1284
-rw-r--r--src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp210
-rw-r--r--src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp1996
-rw-r--r--src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp823
-rw-r--r--src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp52
-rw-r--r--src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm157
-rw-r--r--src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp1232
-rw-r--r--src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp55
-rw-r--r--src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp148
-rw-r--r--src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp246
-rw-r--r--src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp207
-rw-r--r--src/VBox/Runtime/r0drv/nt/symdb.h98
-rw-r--r--src/VBox/Runtime/r0drv/nt/symdbdata.h2998
-rw-r--r--src/VBox/Runtime/r0drv/nt/the-nt-kernel.h99
-rw-r--r--src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp250
-rw-r--r--src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp167
-rw-r--r--src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp159
-rw-r--r--src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp1102
-rw-r--r--src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm52
-rw-r--r--src/VBox/Runtime/r0drv/os2/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/os2/RTR0AssertPanicSystem-r0drv-os2.asm138
-rw-r--r--src/VBox/Runtime/r0drv/os2/RTR0Os2DHQueryDOSVar.asm217
-rw-r--r--src/VBox/Runtime/r0drv/os2/RTR0Os2DHVMGlobalToProcess.asm148
-rw-r--r--src/VBox/Runtime/r0drv/os2/alloc-r0drv-os2.cpp141
-rw-r--r--src/VBox/Runtime/r0drv/os2/assert-r0drv-os2.cpp168
-rw-r--r--src/VBox/Runtime/r0drv/os2/assertA-r0drv-os2.asm81
-rw-r--r--src/VBox/Runtime/r0drv/os2/initterm-r0drv-os2.cpp132
-rw-r--r--src/VBox/Runtime/r0drv/os2/memobj-r0drv-os2.cpp633
-rw-r--r--src/VBox/Runtime/r0drv/os2/memuserkernel-r0drv-os2.cpp101
-rw-r--r--src/VBox/Runtime/r0drv/os2/os2imports.imp121
-rw-r--r--src/VBox/Runtime/r0drv/os2/process-r0drv-os2.cpp88
-rw-r--r--src/VBox/Runtime/r0drv/os2/semevent-r0drv-os2.cpp311
-rw-r--r--src/VBox/Runtime/r0drv/os2/semeventmulti-r0drv-os2.cpp321
-rw-r--r--src/VBox/Runtime/r0drv/os2/semfastmutex-r0drv-os2.cpp149
-rw-r--r--src/VBox/Runtime/r0drv/os2/spinlock-r0drv-os2.cpp165
-rw-r--r--src/VBox/Runtime/r0drv/os2/the-os2-kernel.h93
-rw-r--r--src/VBox/Runtime/r0drv/os2/thread-r0drv-os2.cpp225
-rw-r--r--src/VBox/Runtime/r0drv/os2/thread2-r0drv-os2.cpp120
-rw-r--r--src/VBox/Runtime/r0drv/os2/time-r0drv-os2.cpp126
-rw-r--r--src/VBox/Runtime/r0drv/os2/timer-r0drv-os2.cpp419
-rw-r--r--src/VBox/Runtime/r0drv/os2/timerA-r0drv-os2.asm252
-rw-r--r--src/VBox/Runtime/r0drv/power-r0drv.h54
-rw-r--r--src/VBox/Runtime/r0drv/powernotification-r0drv.c328
-rw-r--r--src/VBox/Runtime/r0drv/solaris/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r0drv/solaris/RTLogWriteDebugger-r0drv-solaris.c76
-rw-r--r--src/VBox/Runtime/r0drv/solaris/RTMpPokeCpu-r0drv-solaris.c60
-rw-r--r--src/VBox/Runtime/r0drv/solaris/alloc-r0drv-solaris.c198
-rw-r--r--src/VBox/Runtime/r0drv/solaris/assert-r0drv-solaris.c87
-rw-r--r--src/VBox/Runtime/r0drv/solaris/dbgkrnlinfo-r0drv-solaris.c349
-rw-r--r--src/VBox/Runtime/r0drv/solaris/initterm-r0drv-solaris.c310
-rw-r--r--src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.c1213
-rw-r--r--src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.h336
-rw-r--r--src/VBox/Runtime/r0drv/solaris/memuserkernel-r0drv-solaris.c110
-rw-r--r--src/VBox/Runtime/r0drv/solaris/modulestub-r0drv-solaris.c89
-rw-r--r--src/VBox/Runtime/r0drv/solaris/mp-r0drv-solaris.c460
-rw-r--r--src/VBox/Runtime/r0drv/solaris/mpnotification-r0drv-solaris.c149
-rw-r--r--src/VBox/Runtime/r0drv/solaris/process-r0drv-solaris.c59
-rw-r--r--src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c364
-rw-r--r--src/VBox/Runtime/r0drv/solaris/semeventmulti-r0drv-solaris.c372
-rw-r--r--src/VBox/Runtime/r0drv/solaris/semeventwait-r0drv-solaris.h508
-rw-r--r--src/VBox/Runtime/r0drv/solaris/semfastmutex-r0drv-solaris.c130
-rw-r--r--src/VBox/Runtime/r0drv/solaris/semmutex-r0drv-solaris.c397
-rw-r--r--src/VBox/Runtime/r0drv/solaris/spinlock-r0drv-solaris.c214
-rw-r--r--src/VBox/Runtime/r0drv/solaris/the-solaris-kernel.h244
-rw-r--r--src/VBox/Runtime/r0drv/solaris/thread-r0drv-solaris.c195
-rw-r--r--src/VBox/Runtime/r0drv/solaris/thread2-r0drv-solaris.c160
-rw-r--r--src/VBox/Runtime/r0drv/solaris/threadctxhooks-r0drv-solaris.c359
-rw-r--r--src/VBox/Runtime/r0drv/solaris/time-r0drv-solaris.c80
-rw-r--r--src/VBox/Runtime/r0drv/solaris/timer-r0drv-solaris.c660
202 files changed, 55990 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r0drv/Makefile.kup b/src/VBox/Runtime/r0drv/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/RTR0DbgKrnlInfoGetSymbol.cpp b/src/VBox/Runtime/r0drv/RTR0DbgKrnlInfoGetSymbol.cpp
new file mode 100644
index 00000000..aecfd3db
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/RTR0DbgKrnlInfoGetSymbol.cpp
@@ -0,0 +1,56 @@
+/* $Id: RTR0DbgKrnlInfoGetSymbol.cpp $ */
+/** @file
+ * IPRT - RTR0DbgKrnlInfoGetSymbol, ring-0 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/dbg.h>
+
+#include <iprt/errcore.h>
+
+
+RTR0DECL(void *) RTR0DbgKrnlInfoGetSymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszSymbol)
+{
+ void *pvSymbol = NULL;
+ int rc = RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, pszModule, pszSymbol, &pvSymbol);
+ if (RT_SUCCESS(rc))
+ return pvSymbol;
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTR0DbgKrnlInfoGetSymbol);
+
diff --git a/src/VBox/Runtime/r0drv/alloc-ef-r0drv.cpp b/src/VBox/Runtime/r0drv/alloc-ef-r0drv.cpp
new file mode 100644
index 00000000..6969e0f6
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/alloc-ef-r0drv.cpp
@@ -0,0 +1,1019 @@
+/* $Id: alloc-ef-r0drv.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, electric fence for ring-0 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTMEM_NO_WRAP_TO_EF_APIS
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/memobj.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+
+#include "internal/mem.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if defined(DOXYGEN_RUNNING)
+# define RTR0MEM_EF_IN_FRONT
+#endif
+
+/** @def RTR0MEM_EF_SIZE
+ * The size of the fence. This must be page aligned.
+ */
+#define RTR0MEM_EF_SIZE PAGE_SIZE
+
+/** @def RTR0MEM_EF_ALIGNMENT
+ * The allocation alignment, power of two of course.
+ *
+ * Use this for working around misaligned sizes, usually stemming from
+ * allocating a string or something after the main structure. When you
+ * encounter this, please fix the allocation to RTMemAllocVar or RTMemAllocZVar.
+ */
+#if 0
+# define RTR0MEM_EF_ALIGNMENT (ARCH_BITS / 8)
+#else
+# define RTR0MEM_EF_ALIGNMENT 1
+#endif
+
+/** @def RTR0MEM_EF_IN_FRONT
+ * Define this to put the fence up in front of the block.
+ * The default (when this isn't defined) is to up it up after the block.
+ */
+//# define RTR0MEM_EF_IN_FRONT
+
+/** @def RTR0MEM_EF_FREE_DELAYED
+ * This define will enable free() delay and protection of the freed data
+ * while it's being delayed. The value of RTR0MEM_EF_FREE_DELAYED defines
+ * the threshold of the delayed blocks.
+ * Delayed blocks does not consume any physical memory, only virtual address space.
+ */
+#define RTR0MEM_EF_FREE_DELAYED (20 * _1M)
+
+/** @def RTR0MEM_EF_FREE_FILL
+ * This define will enable memset(,RTR0MEM_EF_FREE_FILL,)'ing the user memory
+ * in the block before freeing/decommitting it. This is useful in GDB since GDB
+ * appears to be able to read the content of the page even after it's been
+ * decommitted.
+ */
+#define RTR0MEM_EF_FREE_FILL 'f'
+
+/** @def RTR0MEM_EF_FILLER
+ * This define will enable memset(,RTR0MEM_EF_FILLER,)'ing the allocated
+ * memory when the API doesn't require it to be zero'd.
+ */
+#define RTR0MEM_EF_FILLER 0xef
+
+/** @def RTR0MEM_EF_NOMAN_FILLER
+ * This define will enable memset(,RTR0MEM_EF_NOMAN_FILLER,)'ing the
+ * unprotected but not allocated area of memory, the so called no man's land.
+ */
+#define RTR0MEM_EF_NOMAN_FILLER 0xaa
+
+/** @def RTR0MEM_EF_FENCE_FILLER
+ * This define will enable memset(,RTR0MEM_EF_FENCE_FILLER,)'ing the
+ * fence itself, as debuggers can usually read them.
+ */
+#define RTR0MEM_EF_FENCE_FILLER 0xcc
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+#elif !defined(RT_OS_FREEBSD)
+# include <sys/mman.h>
+#endif
+#include <iprt/avl.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Allocation types.
+ */
+typedef enum RTMEMTYPE
+{
+ RTMEMTYPE_RTMEMALLOC,
+ RTMEMTYPE_RTMEMALLOCZ,
+ RTMEMTYPE_RTMEMREALLOC,
+ RTMEMTYPE_RTMEMFREE,
+ RTMEMTYPE_RTMEMFREEZ,
+
+ RTMEMTYPE_NEW,
+ RTMEMTYPE_NEW_ARRAY,
+ RTMEMTYPE_DELETE,
+ RTMEMTYPE_DELETE_ARRAY
+} RTMEMTYPE;
+
+/**
+ * Node tracking a memory allocation.
+ */
+typedef struct RTR0MEMEFBLOCK
+{
+ /** Avl node code, key is the user block pointer. */
+ AVLPVNODECORE Core;
+ /** Allocation type. */
+ RTMEMTYPE enmType;
+ /** The memory object. */
+ RTR0MEMOBJ hMemObj;
+ /** The unaligned size of the block. */
+ size_t cbUnaligned;
+ /** The aligned size of the block. */
+ size_t cbAligned;
+ /** The allocation tag (read-only string). */
+ const char *pszTag;
+ /** The return address of the allocator function. */
+ void *pvCaller;
+ /** Line number of the alloc call. */
+ unsigned iLine;
+ /** File from within the allocation was made. */
+ const char *pszFile;
+ /** Function from within the allocation was made. */
+ const char *pszFunction;
+} RTR0MEMEFBLOCK, *PRTR0MEMEFBLOCK;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Spinlock protecting the all the block's globals. */
+static volatile uint32_t g_BlocksLock;
+/** Tree tracking the allocations. */
+static AVLPVTREE g_BlocksTree;
+
+#ifdef RTR0MEM_EF_FREE_DELAYED
+/** Tail of the delayed blocks. */
+static volatile PRTR0MEMEFBLOCK g_pBlocksDelayHead;
+/** Tail of the delayed blocks. */
+static volatile PRTR0MEMEFBLOCK g_pBlocksDelayTail;
+/** Number of bytes in the delay list (includes fences). */
+static volatile size_t g_cbBlocksDelay;
+#endif /* RTR0MEM_EF_FREE_DELAYED */
+
+/** Array of pointers free watches for. */
+void *gapvRTMemFreeWatch[4] = {NULL, NULL, NULL, NULL};
+/** Enable logging of all freed memory. */
+bool gfRTMemFreeLog = false;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+/**
+ * @callback_method_impl{FNRTSTROUTPUT}
+ */
+static DECLCALLBACK(size_t) rtR0MemEfWrite(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ RT_NOREF1(pvArg);
+ if (cbChars)
+ {
+ RTLogWriteDebugger(pachChars, cbChars);
+ RTLogWriteStdOut(pachChars, cbChars);
+ RTLogWriteUser(pachChars, cbChars);
+ }
+ return cbChars;
+}
+
+
+/**
+ * Complains about something.
+ */
+static void rtR0MemComplain(const char *pszOp, const char *pszFormat, ...)
+{
+ va_list args;
+ RTStrFormat(rtR0MemEfWrite, NULL, NULL, NULL, "RTMem error: %s: ", pszOp);
+ va_start(args, pszFormat);
+ RTStrFormatV(rtR0MemEfWrite, NULL, NULL, NULL, pszFormat, args);
+ va_end(args);
+ RTAssertDoPanic();
+}
+
+/**
+ * Log an event.
+ */
+DECLINLINE(void) rtR0MemLog(const char *pszOp, const char *pszFormat, ...)
+{
+#if 0
+ va_list args;
+ RTStrFormat(rtR0MemEfWrite, NULL, NULL, NULL, "RTMem info: %s: ", pszOp);
+ va_start(args, pszFormat);
+ RTStrFormatV(rtR0MemEfWrite, NULL, NULL, NULL, pszFormat, args);
+ va_end(args);
+#else
+ NOREF(pszOp); NOREF(pszFormat);
+#endif
+}
+
+
+
+/**
+ * Acquires the lock.
+ */
+DECLINLINE(RTCCUINTREG) rtR0MemBlockLock(void)
+{
+ RTCCUINTREG uRet;
+ unsigned c = 0;
+ if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
+ {
+ for (;;)
+ {
+ uRet = ASMIntDisableFlags();
+ if (ASMAtomicCmpXchgU32(&g_BlocksLock, 1, 0))
+ break;
+ ASMSetFlags(uRet);
+ RTThreadSleepNoLog(((++c) >> 2) & 31);
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ uRet = ASMIntDisableFlags();
+ if (ASMAtomicCmpXchgU32(&g_BlocksLock, 1, 0))
+ break;
+ ASMSetFlags(uRet);
+ ASMNopPause();
+ if (++c & 3)
+ ASMNopPause();
+ }
+ }
+ return uRet;
+}
+
+
+/**
+ * Releases the lock.
+ */
+DECLINLINE(void) rtR0MemBlockUnlock(RTCCUINTREG fSavedIntFlags)
+{
+ Assert(g_BlocksLock == 1);
+ ASMAtomicXchgU32(&g_BlocksLock, 0);
+ ASMSetFlags(fSavedIntFlags);
+}
+
+
+/**
+ * Creates a block.
+ */
+DECLINLINE(PRTR0MEMEFBLOCK) rtR0MemBlockCreate(RTMEMTYPE enmType, size_t cbUnaligned, size_t cbAligned,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL)
+{
+ PRTR0MEMEFBLOCK pBlock = (PRTR0MEMEFBLOCK)RTMemAlloc(sizeof(*pBlock));
+ if (pBlock)
+ {
+ pBlock->enmType = enmType;
+ pBlock->cbUnaligned = cbUnaligned;
+ pBlock->cbAligned = cbAligned;
+ pBlock->pszTag = pszTag;
+ pBlock->pvCaller = pvCaller;
+ pBlock->iLine = iLine;
+ pBlock->pszFile = pszFile;
+ pBlock->pszFunction = pszFunction;
+ }
+ return pBlock;
+}
+
+
+/**
+ * Frees a block.
+ */
+DECLINLINE(void) rtR0MemBlockFree(PRTR0MEMEFBLOCK pBlock)
+{
+ RTMemFree(pBlock);
+}
+
+
+/**
+ * Insert a block from the tree.
+ */
+DECLINLINE(void) rtR0MemBlockInsert(PRTR0MEMEFBLOCK pBlock, void *pv, RTR0MEMOBJ hMemObj)
+{
+ pBlock->Core.Key = pv;
+ pBlock->hMemObj = hMemObj;
+ RTCCUINTREG fSavedIntFlags = rtR0MemBlockLock();
+ bool fRc = RTAvlPVInsert(&g_BlocksTree, &pBlock->Core);
+ rtR0MemBlockUnlock(fSavedIntFlags);
+ AssertRelease(fRc);
+}
+
+
+/**
+ * Remove a block from the tree and returns it to the caller.
+ */
+DECLINLINE(PRTR0MEMEFBLOCK) rtR0MemBlockRemove(void *pv)
+{
+ RTCCUINTREG fSavedIntFlags = rtR0MemBlockLock();
+ PRTR0MEMEFBLOCK pBlock = (PRTR0MEMEFBLOCK)RTAvlPVRemove(&g_BlocksTree, pv);
+ rtR0MemBlockUnlock(fSavedIntFlags);
+ return pBlock;
+}
+
+
+/**
+ * Gets a block.
+ */
+DECLINLINE(PRTR0MEMEFBLOCK) rtR0MemBlockGet(void *pv)
+{
+ RTCCUINTREG fSavedIntFlags = rtR0MemBlockLock();
+ PRTR0MEMEFBLOCK pBlock = (PRTR0MEMEFBLOCK)RTAvlPVGet(&g_BlocksTree, pv);
+ rtR0MemBlockUnlock(fSavedIntFlags);
+ return pBlock;
+}
+
+
+/**
+ * Dumps one allocation.
+ */
+static DECLCALLBACK(int) RTMemDumpOne(PAVLPVNODECORE pNode, void *pvUser)
+{
+ PRTR0MEMEFBLOCK pBlock = (PRTR0MEMEFBLOCK)pNode;
+ RTStrFormat(rtR0MemEfWrite, NULL, NULL, NULL, "%p %08lx(+%02lx) %p\n",
+ pBlock->Core.Key,
+ (unsigned long)pBlock->cbUnaligned,
+ (unsigned long)(pBlock->cbAligned - pBlock->cbUnaligned),
+ pBlock->pvCaller);
+ NOREF(pvUser);
+ return 0;
+}
+
+
+/**
+ * Dumps the allocated blocks.
+ * This is something which you should call from gdb.
+ */
+RT_C_DECLS_BEGIN
+void RTMemDump(void);
+RT_C_DECLS_END
+
+void RTMemDump(void)
+{
+ RTStrFormat(rtR0MemEfWrite, NULL, NULL, NULL, "address size(alg) caller\n");
+ RTAvlPVDoWithAll(&g_BlocksTree, true, RTMemDumpOne, NULL);
+}
+
+#ifdef RTR0MEM_EF_FREE_DELAYED
+
+/**
+ * Insert a delayed block.
+ */
+DECLINLINE(void) rtR0MemBlockDelayInsert(PRTR0MEMEFBLOCK pBlock)
+{
+ size_t cbBlock = RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTR0MEM_EF_SIZE;
+ pBlock->Core.pRight = NULL;
+ pBlock->Core.pLeft = NULL;
+ RTCCUINTREG fSavedIntFlags = rtR0MemBlockLock();
+ if (g_pBlocksDelayHead)
+ {
+ g_pBlocksDelayHead->Core.pLeft = (PAVLPVNODECORE)pBlock;
+ pBlock->Core.pRight = (PAVLPVNODECORE)g_pBlocksDelayHead;
+ g_pBlocksDelayHead = pBlock;
+ }
+ else
+ {
+ g_pBlocksDelayTail = pBlock;
+ g_pBlocksDelayHead = pBlock;
+ }
+ g_cbBlocksDelay += cbBlock;
+ rtR0MemBlockUnlock(fSavedIntFlags);
+}
+
+/**
+ * Removes a delayed block.
+ */
+DECLINLINE(PRTR0MEMEFBLOCK) rtR0MemBlockDelayRemove(void)
+{
+ PRTR0MEMEFBLOCK pBlock = NULL;
+ RTCCUINTREG fSavedIntFlags = rtR0MemBlockLock();
+ if (g_cbBlocksDelay > RTR0MEM_EF_FREE_DELAYED)
+ {
+ pBlock = g_pBlocksDelayTail;
+ if (pBlock)
+ {
+ g_pBlocksDelayTail = (PRTR0MEMEFBLOCK)pBlock->Core.pLeft;
+ if (pBlock->Core.pLeft)
+ pBlock->Core.pLeft->pRight = NULL;
+ else
+ g_pBlocksDelayHead = NULL;
+ g_cbBlocksDelay -= RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTR0MEM_EF_SIZE;
+ }
+ }
+ rtR0MemBlockUnlock(fSavedIntFlags);
+ return pBlock;
+}
+
+#endif /* RTR0MEM_EF_FREE_DELAYED */
+
+
+static void rtR0MemFreeBlock(PRTR0MEMEFBLOCK pBlock, const char *pszOp)
+{
+ void *pv = pBlock->Core.Key;
+# ifdef RTR0MEM_EF_IN_FRONT
+ void *pvBlock = (char *)pv - RTR0MEM_EF_SIZE;
+# else
+ void *pvBlock = (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK);
+# endif
+ size_t cbBlock = RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) + RTR0MEM_EF_SIZE;
+
+ int rc = RTR0MemObjProtect(pBlock->hMemObj, 0 /*offSub*/, RT_ALIGN_Z(cbBlock, PAGE_SIZE), RTMEM_PROT_READ | RTMEM_PROT_WRITE);
+ if (RT_FAILURE(rc))
+ rtR0MemComplain(pszOp, "RTR0MemObjProtect([%p], 0, %#x, RTMEM_PROT_READ | RTMEM_PROT_WRITE) -> %Rrc\n",
+ pvBlock, cbBlock, rc);
+
+ rc = RTR0MemObjFree(pBlock->hMemObj, true /*fFreeMappings*/);
+ if (RT_FAILURE(rc))
+ rtR0MemComplain(pszOp, "RTR0MemObjFree([%p LB %#x]) -> %Rrc\n", pvBlock, cbBlock, rc);
+ pBlock->hMemObj = NIL_RTR0MEMOBJ;
+
+ rtR0MemBlockFree(pBlock);
+}
+
+
+/**
+ * Initialize call, we shouldn't fail here.
+ */
+void rtR0MemEfInit(void)
+{
+
+}
+
+/**
+ * @callback_method_impl{AVLPVCALLBACK}
+ */
+static DECLCALLBACK(int) rtR0MemEfDestroyBlock(PAVLPVNODECORE pNode, void *pvUser)
+{
+ PRTR0MEMEFBLOCK pBlock = (PRTR0MEMEFBLOCK)pNode;
+
+ /* Note! pszFile and pszFunction may be invalid at this point. */
+ rtR0MemComplain("rtR0MemEfDestroyBlock", "Leaking %zu bytes at %p (iLine=%u pvCaller=%p)\n",
+ pBlock->cbAligned, pBlock->Core.Key, pBlock->iLine, pBlock->pvCaller);
+
+ rtR0MemFreeBlock(pBlock, "rtR0MemEfDestroyBlock");
+
+ NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Termination call.
+ *
+ * Will check and free memory.
+ */
+void rtR0MemEfTerm(void)
+{
+#ifdef RTR0MEM_EF_FREE_DELAYED
+ /*
+ * Release delayed frees.
+ */
+ RTCCUINTREG fSavedIntFlags = rtR0MemBlockLock();
+ for (;;)
+ {
+ PRTR0MEMEFBLOCK pBlock = g_pBlocksDelayTail;
+ if (pBlock)
+ {
+ g_pBlocksDelayTail = (PRTR0MEMEFBLOCK)pBlock->Core.pLeft;
+ if (pBlock->Core.pLeft)
+ pBlock->Core.pLeft->pRight = NULL;
+ else
+ g_pBlocksDelayHead = NULL;
+ rtR0MemBlockUnlock(fSavedIntFlags);
+
+ rtR0MemFreeBlock(pBlock, "rtR0MemEfTerm");
+
+ rtR0MemBlockLock();
+ }
+ else
+ break;
+ }
+ g_cbBlocksDelay = 0;
+ rtR0MemBlockUnlock(fSavedIntFlags);
+#endif
+
+ /*
+ * Complain about leaks. Then release them.
+ */
+ RTAvlPVDestroy(&g_BlocksTree, rtR0MemEfDestroyBlock, NULL);
+}
+
+
+/**
+ * Internal allocator.
+ */
+static void * rtR0MemAlloc(const char *pszOp, RTMEMTYPE enmType, size_t cbUnaligned, size_t cbAligned,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL)
+{
+ /*
+ * Sanity.
+ */
+ if ( RT_ALIGN_Z(RTR0MEM_EF_SIZE, PAGE_SIZE) != RTR0MEM_EF_SIZE
+ && RTR0MEM_EF_SIZE <= 0)
+ {
+ rtR0MemComplain(pszOp, "Invalid E-fence size! %#x\n", RTR0MEM_EF_SIZE);
+ return NULL;
+ }
+ if (!cbUnaligned)
+ {
+#if 1
+ rtR0MemComplain(pszOp, "Request of ZERO bytes allocation!\n");
+ return NULL;
+#else
+ cbAligned = cbUnaligned = 1;
+#endif
+ }
+
+#ifndef RTR0MEM_EF_IN_FRONT
+ /* Alignment decreases fence accuracy, but this is at least partially
+ * counteracted by filling and checking the alignment padding. When the
+ * fence is in front then then no extra alignment is needed. */
+ cbAligned = RT_ALIGN_Z(cbAligned, RTR0MEM_EF_ALIGNMENT);
+#endif
+
+ /*
+ * Allocate the trace block.
+ */
+ PRTR0MEMEFBLOCK pBlock = rtR0MemBlockCreate(enmType, cbUnaligned, cbAligned, pszTag, pvCaller, RT_SRC_POS_ARGS);
+ if (!pBlock)
+ {
+ rtR0MemComplain(pszOp, "Failed to allocate trace block!\n");
+ return NULL;
+ }
+
+ /*
+ * Allocate a block with page alignment space + the size of the E-fence.
+ */
+ void *pvBlock = NULL;
+ RTR0MEMOBJ hMemObj;
+ size_t cbBlock = RT_ALIGN_Z(cbAligned, PAGE_SIZE) + RTR0MEM_EF_SIZE;
+ int rc = RTR0MemObjAllocPage(&hMemObj, cbBlock, false /*fExecutable*/);
+ if (RT_SUCCESS(rc))
+ pvBlock = RTR0MemObjAddress(hMemObj);
+ if (pvBlock)
+ {
+ /*
+ * Calc the start of the fence and the user block
+ * and then change the page protection of the fence.
+ */
+#ifdef RTR0MEM_EF_IN_FRONT
+ void *pvEFence = pvBlock;
+ void *pv = (char *)pvEFence + RTR0MEM_EF_SIZE;
+# ifdef RTR0MEM_EF_NOMAN_FILLER
+ memset((char *)pv + cbUnaligned, RTR0MEM_EF_NOMAN_FILLER, cbBlock - RTR0MEM_EF_SIZE - cbUnaligned);
+# endif
+#else
+ void *pvEFence = (char *)pvBlock + (cbBlock - RTR0MEM_EF_SIZE);
+ void *pv = (char *)pvEFence - cbAligned;
+# ifdef RTR0MEM_EF_NOMAN_FILLER
+ memset(pvBlock, RTR0MEM_EF_NOMAN_FILLER, cbBlock - RTR0MEM_EF_SIZE - cbAligned);
+ memset((char *)pv + cbUnaligned, RTR0MEM_EF_NOMAN_FILLER, cbAligned - cbUnaligned);
+# endif
+#endif
+
+#ifdef RTR0MEM_EF_FENCE_FILLER
+ memset(pvEFence, RTR0MEM_EF_FENCE_FILLER, RTR0MEM_EF_SIZE);
+#endif
+ rc = RTR0MemObjProtect(hMemObj, (uint8_t *)pvEFence - (uint8_t *)pvBlock, RTR0MEM_EF_SIZE, RTMEM_PROT_NONE);
+ if (!rc)
+ {
+ rtR0MemBlockInsert(pBlock, pv, hMemObj);
+ if (enmType == RTMEMTYPE_RTMEMALLOCZ)
+ memset(pv, 0, cbUnaligned);
+#ifdef RTR0MEM_EF_FILLER
+ else
+ memset(pv, RTR0MEM_EF_FILLER, cbUnaligned);
+#endif
+
+ rtR0MemLog(pszOp, "returns %p (pvBlock=%p cbBlock=%#x pvEFence=%p cbUnaligned=%#x)\n", pv, pvBlock, cbBlock, pvEFence, cbUnaligned);
+ return pv;
+ }
+ rtR0MemComplain(pszOp, "RTMemProtect failed, pvEFence=%p size %d, rc=%d\n", pvEFence, RTR0MEM_EF_SIZE, rc);
+ RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
+ }
+ else
+ {
+ rtR0MemComplain(pszOp, "Failed to allocated %zu (%zu) bytes (rc=%Rrc).\n", cbBlock, cbUnaligned, rc);
+ if (RT_SUCCESS(rc))
+ RTR0MemObjFree(hMemObj, true /*fFreeMappings*/);
+ }
+
+ rtR0MemBlockFree(pBlock);
+ return NULL;
+}
+
+
+/**
+ * Internal free.
+ */
+static void rtR0MemFree(const char *pszOp, RTMEMTYPE enmType, void *pv, size_t cbUser, void *pvCaller, RT_SRC_POS_DECL)
+{
+ NOREF(enmType); RT_SRC_POS_NOREF();
+
+ /*
+ * Simple case.
+ */
+ if (!pv)
+ return;
+
+ /*
+ * Check watch points.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(gapvRTMemFreeWatch); i++)
+ if (gapvRTMemFreeWatch[i] == pv)
+ RTAssertDoPanic();
+
+ /*
+ * Find the block.
+ */
+ PRTR0MEMEFBLOCK pBlock = rtR0MemBlockRemove(pv);
+ if (pBlock)
+ {
+ if (gfRTMemFreeLog)
+ RTLogPrintf("RTMem %s: pv=%p pvCaller=%p cbUnaligned=%#x\n", pszOp, pv, pvCaller, pBlock->cbUnaligned);
+
+#ifdef RTR0MEM_EF_NOMAN_FILLER
+ /*
+ * Check whether the no man's land is untouched.
+ */
+# ifdef RTR0MEM_EF_IN_FRONT
+ void *pvWrong = ASMMemFirstMismatchingU8((char *)pv + pBlock->cbUnaligned,
+ RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) - pBlock->cbUnaligned,
+ RTR0MEM_EF_NOMAN_FILLER);
+# else
+ /* Alignment must match allocation alignment in rtMemAlloc(). */
+ void *pvWrong = ASMMemFirstMismatchingU8((char *)pv + pBlock->cbUnaligned,
+ pBlock->cbAligned - pBlock->cbUnaligned,
+ RTR0MEM_EF_NOMAN_FILLER);
+ if (pvWrong)
+ RTAssertDoPanic();
+ pvWrong = ASMMemFirstMismatchingU8((void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK),
+ RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE) - pBlock->cbAligned,
+ RTR0MEM_EF_NOMAN_FILLER);
+# endif
+ if (pvWrong)
+ RTAssertDoPanic();
+#endif
+
+ /*
+ * Fill the user part of the block.
+ */
+ AssertMsg(enmType != RTMEMTYPE_RTMEMFREEZ || cbUser == pBlock->cbUnaligned,
+ ("cbUser=%#zx cbUnaligned=%#zx\n", cbUser, pBlock->cbUnaligned));
+ RT_NOREF(cbUser);
+ if (enmType == RTMEMTYPE_RTMEMFREEZ)
+ RT_BZERO(pv, pBlock->cbUnaligned);
+#ifdef RTR0MEM_EF_FREE_FILL
+ else
+ memset(pv, RTR0MEM_EF_FREE_FILL, pBlock->cbUnaligned);
+#endif
+
+#if defined(RTR0MEM_EF_FREE_DELAYED) && RTR0MEM_EF_FREE_DELAYED > 0
+ /*
+ * We're doing delayed freeing.
+ * That means we'll expand the E-fence to cover the entire block.
+ */
+ int rc = RTR0MemObjProtect(pBlock->hMemObj,
+# ifdef RTR0MEM_EF_IN_FRONT
+ RTR0MEM_EF_SIZE,
+# else
+ 0 /*offSub*/,
+# endif
+ RT_ALIGN_Z(pBlock->cbAligned, PAGE_SIZE),
+ RTMEM_PROT_NONE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Insert it into the free list and process pending frees.
+ */
+ rtR0MemBlockDelayInsert(pBlock);
+ while ((pBlock = rtR0MemBlockDelayRemove()) != NULL)
+ rtR0MemFreeBlock(pBlock, pszOp);
+ }
+ else
+ rtR0MemComplain(pszOp, "Failed to expand the efence of pv=%p cb=%d, rc=%d.\n", pv, pBlock, rc);
+
+#else /* !RTR0MEM_EF_FREE_DELAYED */
+ rtR0MemFreeBlock(pBlock, pszOp);
+#endif /* !RTR0MEM_EF_FREE_DELAYED */
+ }
+ else
+ rtR0MemComplain(pszOp, "pv=%p not found! Incorrect free!\n", pv);
+}
+
+
+/**
+ * Internal realloc.
+ */
+static void *rtR0MemRealloc(const char *pszOp, RTMEMTYPE enmType, void *pvOld, size_t cbNew,
+ const char *pszTag, void *pvCaller, RT_SRC_POS_DECL)
+{
+ /*
+ * Allocate new and copy.
+ */
+ if (!pvOld)
+ return rtR0MemAlloc(pszOp, enmType, cbNew, cbNew, pszTag, pvCaller, RT_SRC_POS_ARGS);
+ if (!cbNew)
+ {
+ rtR0MemFree(pszOp, RTMEMTYPE_RTMEMREALLOC, pvOld, 0, pvCaller, RT_SRC_POS_ARGS);
+ return NULL;
+ }
+
+ /*
+ * Get the block, allocate the new, copy the data, free the old one.
+ */
+ PRTR0MEMEFBLOCK pBlock = rtR0MemBlockGet(pvOld);
+ if (pBlock)
+ {
+ void *pvRet = rtR0MemAlloc(pszOp, enmType, cbNew, cbNew, pszTag, pvCaller, RT_SRC_POS_ARGS);
+ if (pvRet)
+ {
+ memcpy(pvRet, pvOld, RT_MIN(cbNew, pBlock->cbUnaligned));
+ rtR0MemFree(pszOp, RTMEMTYPE_RTMEMREALLOC, pvOld, 0, pvCaller, RT_SRC_POS_ARGS);
+ }
+ return pvRet;
+ }
+ rtR0MemComplain(pszOp, "pvOld=%p was not found!\n", pvOld);
+ return NULL;
+}
+
+
+
+
+RTDECL(void *) RTMemEfTmpAlloc(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("TmpAlloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfTmpAllocZ(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("TmpAlloc", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void) RTMemEfTmpFree(void *pv, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void) RTMemEfTmpFreeZ(void *pv, size_t cb, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("FreeZ", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAlloc(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAllocZ(size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAllocVar(size_t cbUnaligned, const char *pszTag, RT_SRC_POS_DECL) 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 rtR0MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfAllocZVar(size_t cbUnaligned, const char *pszTag, RT_SRC_POS_DECL) 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 rtR0MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfRealloc(void *pvOld, size_t cbNew, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ return rtR0MemRealloc("Realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+RTDECL(void *) RTMemEfReallocZ(void *pvOld, size_t cbOld, size_t cbNew, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ void *pvDst = rtR0MemRealloc("Realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), RT_SRC_POS_ARGS);
+ if (pvDst && cbNew > cbOld)
+ memset((uint8_t *)pvDst + cbOld, 0, cbNew - cbOld);
+ return pvDst;
+}
+
+
+RTDECL(void) RTMemEfFree(void *pv, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void) RTMemEfFreeZ(void *pv, size_t cb, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("Free", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), RT_SRC_POS_ARGS);
+}
+
+
+RTDECL(void *) RTMemEfDup(const void *pvSrc, size_t cb, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cb, pszTag, RT_SRC_POS_ARGS);
+ if (pvDst)
+ memcpy(pvDst, pvSrc, cb);
+ return pvDst;
+}
+
+
+RTDECL(void *) RTMemEfDupEx(const void *pvSrc, size_t cbSrc, size_t cbExtra, const char *pszTag, RT_SRC_POS_DECL) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cbSrc + cbExtra, pszTag, RT_SRC_POS_ARGS);
+ if (pvDst)
+ {
+ memcpy(pvDst, pvSrc, cbSrc);
+ memset((uint8_t *)pvDst + cbSrc, 0, cbExtra);
+ }
+ return pvDst;
+}
+
+
+
+
+/*
+ *
+ * The NP (no position) versions.
+ *
+ */
+
+
+
+RTDECL(void *) RTMemEfTmpAllocNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("TmpAlloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfTmpAllocZNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("TmpAllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void) RTMemEfTmpFreeNP(void *pv) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void) RTMemEfTmpFreeZNP(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("FreeZ", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocZNP(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR0MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cb, cb, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocVarNP(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 rtR0MemAlloc("Alloc", RTMEMTYPE_RTMEMALLOC, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfAllocZVarNP(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 rtR0MemAlloc("AllocZ", RTMEMTYPE_RTMEMALLOCZ, cbUnaligned, cbAligned, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfReallocNP(void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ return rtR0MemRealloc("Realloc", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfReallocZNP(void *pvOld, size_t cbOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvDst = rtR0MemRealloc("ReallocZ", RTMEMTYPE_RTMEMREALLOC, pvOld, cbNew, pszTag, ASMReturnAddress(), NULL, 0, NULL);
+ if (pvDst && cbNew > cbOld)
+ memset((uint8_t *)pvDst + cbOld, 0, cbNew - cbOld);
+ return pvDst;
+}
+
+
+RTDECL(void) RTMemEfFreeNP(void *pv) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("Free", RTMEMTYPE_RTMEMFREE, pv, 0, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void) RTMemEfFreeZNP(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ if (pv)
+ rtR0MemFree("FreeZ", RTMEMTYPE_RTMEMFREEZ, pv, cb, ASMReturnAddress(), NULL, 0, NULL);
+}
+
+
+RTDECL(void *) RTMemEfDupNP(const void *pvSrc, size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cb, pszTag, NULL, 0, NULL);
+ if (pvDst)
+ memcpy(pvDst, pvSrc, cb);
+ return pvDst;
+}
+
+
+RTDECL(void *) RTMemEfDupExNP(const void *pvSrc, size_t cbSrc, size_t cbExtra, const char *pszTag) RT_NO_THROW_DEF
+{
+ void *pvDst = RTMemEfAlloc(cbSrc + cbExtra, pszTag, NULL, 0, NULL);
+ if (pvDst)
+ {
+ memcpy(pvDst, pvSrc, cbSrc);
+ memset((uint8_t *)pvDst + cbSrc, 0, cbExtra);
+ }
+ return pvDst;
+}
+
diff --git a/src/VBox/Runtime/r0drv/alloc-r0drv.cpp b/src/VBox/Runtime/r0drv/alloc-r0drv.cpp
new file mode 100644
index 00000000..f00f31d8
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/alloc-r0drv.cpp
@@ -0,0 +1,421 @@
+/* $Id: alloc-r0drv.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTMEM_NO_WRAP_TO_EF_APIS
+#include <iprt/mem.h>
+#include "internal/iprt.h"
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#ifdef RT_MORE_STRICT
+# include <iprt/mp.h>
+#endif
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "r0drv/alloc-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef RT_STRICT
+# define RTR0MEM_STRICT
+#endif
+
+#ifdef RTR0MEM_STRICT
+# define RTR0MEM_FENCE_EXTRA 16
+#else
+# define RTR0MEM_FENCE_EXTRA 0
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef RTR0MEM_STRICT
+/** Fence data. */
+static uint8_t const g_abFence[RTR0MEM_FENCE_EXTRA] =
+{
+ 0x77, 0x88, 0x66, 0x99, 0x55, 0xaa, 0x44, 0xbb,
+ 0x33, 0xcc, 0x22, 0xdd, 0x11, 0xee, 0x00, 0xff
+};
+#endif
+
+
+/**
+ * Wrapper around rtR0MemAllocEx.
+ *
+ * @returns Pointer to the allocated memory block header.
+ * @param cb The number of bytes to allocate (sans header).
+ * @param fFlags The allocation flags.
+ */
+DECLINLINE(PRTMEMHDR) rtR0MemAlloc(size_t cb, uint32_t fFlags)
+{
+ PRTMEMHDR pHdr;
+ int rc = rtR0MemAllocEx(cb, fFlags, &pHdr);
+ if (RT_FAILURE(rc))
+ return NULL;
+ return pHdr;
+}
+
+
+RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return RTMemAllocTag(cb, pszTag);
+}
+RT_EXPORT_SYMBOL(RTMemTmpAllocTag);
+
+
+RTDECL(void *) RTMemTmpAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ return RTMemAllocZTag(cb, pszTag);
+}
+RT_EXPORT_SYMBOL(RTMemTmpAllocZTag);
+
+
+RTDECL(void) RTMemTmpFree(void *pv) RT_NO_THROW_DEF
+{
+ return RTMemFree(pv);
+}
+RT_EXPORT_SYMBOL(RTMemTmpFree);
+
+
+RTDECL(void) RTMemTmpFreeZ(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ return RTMemFreeZ(pv, cb);
+}
+RT_EXPORT_SYMBOL(RTMemTmpFreeZ);
+
+
+
+
+
+RTDECL(void *) RTMemAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ PRTMEMHDR pHdr;
+ RT_ASSERT_INTS_ON();
+ RT_NOREF_PV(pszTag);
+
+ pHdr = rtR0MemAlloc(cb + RTR0MEM_FENCE_EXTRA, 0);
+ if (pHdr)
+ {
+#ifdef RTR0MEM_STRICT
+ pHdr->cbReq = (uint32_t)cb; Assert(pHdr->cbReq == cb);
+ memcpy((uint8_t *)(pHdr + 1) + cb, &g_abFence[0], RTR0MEM_FENCE_EXTRA);
+#endif
+ return pHdr + 1;
+ }
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTMemAllocTag);
+
+
+RTDECL(void *) RTMemAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ PRTMEMHDR pHdr;
+ RT_ASSERT_INTS_ON();
+ RT_NOREF_PV(pszTag);
+
+ pHdr = rtR0MemAlloc(cb + RTR0MEM_FENCE_EXTRA, RTMEMHDR_FLAG_ZEROED);
+ if (pHdr)
+ {
+#ifdef RTR0MEM_STRICT
+ pHdr->cbReq = (uint32_t)cb; Assert(pHdr->cbReq == cb);
+ memcpy((uint8_t *)(pHdr + 1) + cb, &g_abFence[0], RTR0MEM_FENCE_EXTRA);
+ return memset(pHdr + 1, 0, cb);
+#else
+ return memset(pHdr + 1, 0, pHdr->cb);
+#endif
+ }
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTMemAllocZTag);
+
+
+RTDECL(void *) RTMemAllocVarTag(size_t cbUnaligned, const char *pszTag)
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return RTMemAllocTag(cbAligned, pszTag);
+}
+RT_EXPORT_SYMBOL(RTMemAllocVarTag);
+
+
+RTDECL(void *) RTMemAllocZVarTag(size_t cbUnaligned, const char *pszTag)
+{
+ size_t cbAligned;
+ if (cbUnaligned >= 16)
+ cbAligned = RT_ALIGN_Z(cbUnaligned, 16);
+ else
+ cbAligned = RT_ALIGN_Z(cbUnaligned, sizeof(void *));
+ return RTMemAllocZTag(cbAligned, pszTag);
+}
+RT_EXPORT_SYMBOL(RTMemAllocZVarTag);
+
+
+RTDECL(void *) RTMemReallocTag(void *pvOld, size_t cbNew, const char *pszTag) RT_NO_THROW_DEF
+{
+ PRTMEMHDR pHdrOld;
+
+ /* Free. */
+ if (!cbNew && pvOld)
+ {
+ RTMemFree(pvOld);
+ return NULL;
+ }
+
+ /* Alloc. */
+ if (!pvOld)
+ return RTMemAllocTag(cbNew, pszTag);
+
+ /*
+ * Realloc.
+ */
+ pHdrOld = (PRTMEMHDR)pvOld - 1;
+ RT_ASSERT_PREEMPTIBLE();
+
+ if (pHdrOld->u32Magic == RTMEMHDR_MAGIC)
+ {
+ PRTMEMHDR pHdrNew;
+
+ /* If there is sufficient space in the old block and we don't cause
+ substantial internal fragmentation, reuse the old block. */
+ if ( pHdrOld->cb >= cbNew + RTR0MEM_FENCE_EXTRA
+ && pHdrOld->cb - (cbNew + RTR0MEM_FENCE_EXTRA) <= 128)
+ {
+ pHdrOld->cbReq = (uint32_t)cbNew; Assert(pHdrOld->cbReq == cbNew);
+#ifdef RTR0MEM_STRICT
+ memcpy((uint8_t *)(pHdrOld + 1) + cbNew, &g_abFence[0], RTR0MEM_FENCE_EXTRA);
+#endif
+ return pvOld;
+ }
+
+ /* Allocate a new block and copy over the content. */
+ pHdrNew = rtR0MemAlloc(cbNew + RTR0MEM_FENCE_EXTRA, 0);
+ if (pHdrNew)
+ {
+ size_t cbCopy = RT_MIN(pHdrOld->cb, pHdrNew->cb);
+ memcpy(pHdrNew + 1, pvOld, cbCopy);
+#ifdef RTR0MEM_STRICT
+ pHdrNew->cbReq = (uint32_t)cbNew; Assert(pHdrNew->cbReq == cbNew);
+ memcpy((uint8_t *)(pHdrNew + 1) + cbNew, &g_abFence[0], RTR0MEM_FENCE_EXTRA);
+ AssertReleaseMsg(!memcmp((uint8_t *)(pHdrOld + 1) + pHdrOld->cbReq, &g_abFence[0], RTR0MEM_FENCE_EXTRA),
+ ("pHdr=%p pvOld=%p cbReq=%u cb=%u cbNew=%zu fFlags=%#x\n"
+ "fence: %.*Rhxs\n"
+ "expected: %.*Rhxs\n",
+ pHdrOld, pvOld, pHdrOld->cbReq, pHdrOld->cb, cbNew, pHdrOld->fFlags,
+ RTR0MEM_FENCE_EXTRA, (uint8_t *)(pHdrOld + 1) + pHdrOld->cbReq,
+ RTR0MEM_FENCE_EXTRA, &g_abFence[0]));
+#endif
+ rtR0MemFree(pHdrOld);
+ return pHdrNew + 1;
+ }
+ }
+ else
+ AssertMsgFailed(("pHdrOld->u32Magic=%RX32 pvOld=%p cbNew=%#zx\n", pHdrOld->u32Magic, pvOld, cbNew));
+
+ return NULL;
+}
+RT_EXPORT_SYMBOL(RTMemReallocTag);
+
+
+RTDECL(void) RTMemFree(void *pv) RT_NO_THROW_DEF
+{
+ PRTMEMHDR pHdr;
+ RT_ASSERT_INTS_ON();
+
+ if (!pv)
+ return;
+ pHdr = (PRTMEMHDR)pv - 1;
+ if (pHdr->u32Magic == RTMEMHDR_MAGIC)
+ {
+ Assert(!(pHdr->fFlags & RTMEMHDR_FLAG_ALLOC_EX));
+#ifdef RTR0MEM_STRICT
+ AssertReleaseMsg(!memcmp((uint8_t *)(pHdr + 1) + pHdr->cbReq, &g_abFence[0], RTR0MEM_FENCE_EXTRA),
+ ("pHdr=%p pv=%p cbReq=%u cb=%u fFlags=%#x\n"
+ "fence: %.*Rhxs\n"
+ "expected: %.*Rhxs\n",
+ pHdr, pv, pHdr->cbReq, pHdr->cb, pHdr->fFlags,
+ RTR0MEM_FENCE_EXTRA, (uint8_t *)(pHdr + 1) + pHdr->cbReq,
+ RTR0MEM_FENCE_EXTRA, &g_abFence[0]));
+#endif
+ rtR0MemFree(pHdr);
+ }
+ else
+ AssertMsgFailed(("pHdr->u32Magic=%RX32 pv=%p\n", pHdr->u32Magic, pv));
+}
+RT_EXPORT_SYMBOL(RTMemFree);
+
+
+RTDECL(void) RTMemFreeZ(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ PRTMEMHDR pHdr;
+ RT_ASSERT_INTS_ON();
+
+ if (!pv)
+ return;
+ pHdr = (PRTMEMHDR)pv - 1;
+ if (pHdr->u32Magic == RTMEMHDR_MAGIC)
+ {
+ Assert(!(pHdr->fFlags & RTMEMHDR_FLAG_ALLOC_EX));
+#ifdef RTR0MEM_STRICT
+ AssertReleaseMsg(!memcmp((uint8_t *)(pHdr + 1) + pHdr->cbReq, &g_abFence[0], RTR0MEM_FENCE_EXTRA),
+ ("pHdr=%p pv=%p cbReq=%u cb=%u fFlags=%#x\n"
+ "fence: %.*Rhxs\n"
+ "expected: %.*Rhxs\n",
+ pHdr, pv, pHdr->cbReq, pHdr->cb, pHdr->fFlags,
+ RTR0MEM_FENCE_EXTRA, (uint8_t *)(pHdr + 1) + pHdr->cbReq,
+ RTR0MEM_FENCE_EXTRA, &g_abFence[0]));
+#endif
+ AssertMsgStmt(cb == pHdr->cbReq, ("cb=%#zx cbReq=%#x\n", cb, pHdr->cbReq), cb = pHdr->cbReq);
+ RT_BZERO(pv, cb);
+ rtR0MemFree(pHdr);
+ }
+ else
+ AssertMsgFailed(("pHdr->u32Magic=%RX32 pv=%p\n", pHdr->u32Magic, pv));
+}
+RT_EXPORT_SYMBOL(RTMemFreeZ);
+
+
+RTDECL(int) RTMemAllocExTag(size_t cb, size_t cbAlignment, uint32_t fFlags, const char *pszTag, void **ppv) RT_NO_THROW_DEF
+{
+ uint32_t fHdrFlags = RTMEMHDR_FLAG_ALLOC_EX;
+ PRTMEMHDR pHdr;
+ int rc;
+ RT_NOREF_PV(pszTag);
+
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+ if (!(fFlags & RTMEMALLOCEX_FLAGS_ANY_CTX_ALLOC))
+ RT_ASSERT_INTS_ON();
+ AssertReturn(!(fFlags & RTMEMALLOCEX_FLAGS_EXEC), VERR_INVALID_FLAGS);
+
+ /*
+ * Fake up some alignment support.
+ */
+ AssertMsgReturn(cbAlignment <= sizeof(void *), ("%zu (%#x)\n", cbAlignment, cbAlignment), VERR_UNSUPPORTED_ALIGNMENT);
+ if (cb < cbAlignment)
+ cb = cbAlignment;
+
+ /*
+ * Validate and convert flags.
+ */
+ AssertMsgReturn(!(fFlags & ~RTMEMALLOCEX_FLAGS_VALID_MASK_R0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ if (fFlags & RTMEMALLOCEX_FLAGS_ZEROED)
+ fHdrFlags |= RTMEMHDR_FLAG_ZEROED;
+ if (fFlags & RTMEMALLOCEX_FLAGS_ANY_CTX_ALLOC)
+ fHdrFlags |= RTMEMHDR_FLAG_ANY_CTX_ALLOC;
+ if (fFlags & RTMEMALLOCEX_FLAGS_ANY_CTX_FREE)
+ fHdrFlags |= RTMEMHDR_FLAG_ANY_CTX_FREE;
+
+ /*
+ * Do the allocation.
+ */
+ rc = rtR0MemAllocEx(cb + RTR0MEM_FENCE_EXTRA, fHdrFlags, &pHdr);
+ if (RT_SUCCESS(rc))
+ {
+ void *pv;
+
+ Assert(pHdr->cbReq == cb + RTR0MEM_FENCE_EXTRA);
+ Assert((pHdr->fFlags & fFlags) == fFlags);
+
+ /*
+ * Calc user pointer, initialize the memory if requested, and if
+ * memory strictness is enable set up the fence.
+ */
+ pv = pHdr + 1;
+ *ppv = pv;
+ if (fFlags & RTMEMHDR_FLAG_ZEROED)
+ memset(pv, 0, pHdr->cb);
+
+#ifdef RTR0MEM_STRICT
+ pHdr->cbReq = (uint32_t)cb;
+ memcpy((uint8_t *)pv + cb, &g_abFence[0], RTR0MEM_FENCE_EXTRA);
+#endif
+ }
+
+ RT_ASSERT_PREEMPT_CPUID();
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTMemAllocExTag);
+
+
+RTDECL(void) RTMemFreeEx(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ PRTMEMHDR pHdr;
+ RT_NOREF_PV(cb);
+
+ if (!pv)
+ return;
+
+ AssertPtr(pv);
+ pHdr = (PRTMEMHDR)pv - 1;
+ if (pHdr->u32Magic == RTMEMHDR_MAGIC)
+ {
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+
+ Assert(pHdr->fFlags & RTMEMHDR_FLAG_ALLOC_EX);
+ if (!(pHdr->fFlags & RTMEMHDR_FLAG_ANY_CTX_FREE))
+ RT_ASSERT_INTS_ON();
+ AssertMsg(pHdr->cbReq == cb, ("cbReq=%zu cb=%zu\n", pHdr->cb, cb));
+
+#ifdef RTR0MEM_STRICT
+ AssertReleaseMsg(!memcmp((uint8_t *)(pHdr + 1) + pHdr->cbReq, &g_abFence[0], RTR0MEM_FENCE_EXTRA),
+ ("pHdr=%p pv=%p cbReq=%u cb=%u fFlags=%#x\n"
+ "fence: %.*Rhxs\n"
+ "expected: %.*Rhxs\n",
+ pHdr, pv, pHdr->cbReq, pHdr->cb, pHdr->fFlags,
+ RTR0MEM_FENCE_EXTRA, (uint8_t *)(pHdr + 1) + pHdr->cbReq,
+ RTR0MEM_FENCE_EXTRA, &g_abFence[0]));
+#endif
+ rtR0MemFree(pHdr);
+ RT_ASSERT_PREEMPT_CPUID();
+ }
+ else
+ AssertMsgFailed(("pHdr->u32Magic=%RX32 pv=%p\n", pHdr->u32Magic, pv));
+}
+RT_EXPORT_SYMBOL(RTMemFreeEx);
+
diff --git a/src/VBox/Runtime/r0drv/alloc-r0drv.h b/src/VBox/Runtime/r0drv/alloc-r0drv.h
new file mode 100644
index 00000000..4383882a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/alloc-r0drv.h
@@ -0,0 +1,111 @@
+/* $Id: alloc-r0drv.h $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 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
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r0drv_alloc_r0drv_h
+#define IPRT_INCLUDED_SRC_r0drv_alloc_r0drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/mem.h>
+#include "internal/magics.h"
+
+RT_C_DECLS_BEGIN
+
+/**
+ * Header which heading all memory blocks.
+ */
+typedef struct RTMEMHDR
+{
+ /** Magic (RTMEMHDR_MAGIC). */
+ uint32_t u32Magic;
+ /** Block flags (RTMEMHDR_FLAG_*). */
+ uint32_t fFlags;
+ /** The actual size of the block, header not included. */
+ uint32_t cb;
+ /** The requested allocation size. */
+ uint32_t cbReq;
+} RTMEMHDR, *PRTMEMHDR;
+
+
+/** @name RTMEMHDR::fFlags.
+ * @{ */
+/** Clear the allocated memory. */
+#define RTMEMHDR_FLAG_ZEROED RT_BIT(0)
+/** Use allocation method suitable for any context. */
+#define RTMEMHDR_FLAG_ANY_CTX_ALLOC RT_BIT(1)
+/** Use allocation method which allow for freeing in any context. */
+#define RTMEMHDR_FLAG_ANY_CTX_FREE RT_BIT(2)
+/** Both alloc and free in any context (or we're just darn lazy). */
+#define RTMEMHDR_FLAG_ANY_CTX (RTMEMHDR_FLAG_ANY_CTX_ALLOC | RTMEMHDR_FLAG_ANY_CTX_FREE)
+/** Indicate that it was allocated by rtR0MemAllocExTag. */
+#define RTMEMHDR_FLAG_ALLOC_EX RT_BIT(3)
+#ifdef RT_OS_LINUX
+/** Linux: Allocated by kmalloc() instead of vmalloc(). */
+# define RTMEMHDR_FLAG_KMALLOC RT_BIT(31)
+#elif defined(RT_OS_WINDOWS)
+/** Windows: Untagged allocation by ExAllocatePool, freed using ExFreePool. */
+# define RTMEMHDR_FLAG_UNTAGGED RT_BIT(31)
+#endif
+/** @} */
+
+
+/**
+ * Heap allocation back end for ring-0.
+ *
+ * @returns IPRT status code. VERR_NO_MEMORY suffices for RTMEMHDR_FLAG_EXEC,
+ * the caller will change it to VERR_NO_EXEC_MEMORY when appropriate.
+ *
+ * @param cb The amount of memory requested by the user. This does
+ * not include the header.
+ * @param fFlags The allocation flags and more. These should be
+ * assigned to RTMEMHDR::fFlags together with any flags
+ * the backend might be using.
+ * @param ppHdr Where to return the memory header on success.
+ */
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr);
+
+/**
+ * Free memory allocated by rtR0MemAllocEx.
+ * @param pHdr The memory block to free. (Never NULL.)
+ */
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr);
+
+RT_C_DECLS_END
+#endif /* !IPRT_INCLUDED_SRC_r0drv_alloc_r0drv_h */
+
diff --git a/src/VBox/Runtime/r0drv/darwin/Makefile.kup b/src/VBox/Runtime/r0drv/darwin/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/darwin/RTLogWriteDebugger-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/RTLogWriteDebugger-r0drv-darwin.cpp
new file mode 100644
index 00000000..aba8708c
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/RTLogWriteDebugger-r0drv-darwin.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTLogWriteDebugger-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Log To Debugger, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/log.h>
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ kprintf("%.*s", (int)cb, pch);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/RTLogWriteStdOut-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/RTLogWriteStdOut-r0drv-darwin.cpp
new file mode 100644
index 00000000..6b4c1e4d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/RTLogWriteStdOut-r0drv-darwin.cpp
@@ -0,0 +1,52 @@
+/* $Id: RTLogWriteStdOut-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Log To StdOut, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/log.h>
+
+
+RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ printf("%.*s", (int)cb, pch);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/alloc-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/alloc-r0drv-darwin.cpp
new file mode 100644
index 00000000..0835e27b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/alloc-r0drv-darwin.cpp
@@ -0,0 +1,152 @@
+/* $Id: alloc-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations" /* (IOMallocContiguous et al are deprecated) */
+#include "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+#include <iprt/memobj.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/thread.h>
+#include "r0drv/alloc-r0drv.h"
+
+
+
+/**
+ * OS specific allocation function.
+ */
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ if (RT_LIKELY(!(fFlags & RTMEMHDR_FLAG_ANY_CTX)))
+ {
+ PRTMEMHDR pHdr = (PRTMEMHDR)IOMalloc(cb + sizeof(*pHdr));
+ if (RT_LIKELY(pHdr))
+ {
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = cb;
+ pHdr->cbReq = cb;
+ *ppHdr = pHdr;
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ printf("rtR0MemAllocEx(%#zx, %#x) failed\n", cb + sizeof(*pHdr), fFlags);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * OS specific free function.
+ */
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ pHdr->u32Magic += 1;
+ IOFree(pHdr, pHdr->cb + sizeof(*pHdr));
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ /*
+ * validate input.
+ */
+ AssertPtr(pPhys);
+ Assert(cb > 0);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Allocate the memory and ensure that the API is still providing
+ * memory that's always below 4GB.
+ */
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+ IOPhysicalAddress PhysAddr;
+ void *pv = IOMallocContiguous(cb, PAGE_SIZE, &PhysAddr);
+ if (pv)
+ {
+ if (PhysAddr + (cb - 1) <= (IOPhysicalAddress)0xffffffff)
+ {
+ if (!((uintptr_t)pv & PAGE_OFFSET_MASK))
+ {
+ *pPhys = PhysAddr;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return pv;
+ }
+ AssertMsgFailed(("IOMallocContiguous didn't return a page aligned address - %p!\n", pv));
+ }
+ else
+ AssertMsgFailed(("IOMallocContiguous returned high address! PhysAddr=%RX64 cb=%#zx\n", (uint64_t)PhysAddr, cb));
+ IOFreeContiguous(pv, cb);
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return NULL;
+}
+
+
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ if (pv)
+ {
+ Assert(cb > 0);
+ AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv));
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+ IOFreeContiguous(pv, cb);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/assert-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/assert-r0drv-darwin.cpp
new file mode 100644
index 00000000..61d82283
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/assert-r0drv-darwin.cpp
@@ -0,0 +1,87 @@
+/* $Id: assert-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/assert.h>
+
+#include <iprt/asm.h>
+#include <iprt/log.h>
+#include <iprt/stdarg.h>
+#include <iprt/string.h>
+
+#include "internal/assert.h"
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ kprintf("\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%u) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+ printf("\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%u) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ char szMsg[256];
+
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+ kprintf("%s", szMsg);
+ printf("%s", szMsg);
+
+ NOREF(fInitial);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+RTR0DECL(void) RTR0AssertPanicSystem(void)
+{
+ panic("%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/dbgkrnlinfo-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/dbgkrnlinfo-r0drv-darwin.cpp
new file mode 100644
index 00000000..3ef1b1f6
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/dbgkrnlinfo-r0drv-darwin.cpp
@@ -0,0 +1,1576 @@
+/* $Id: dbgkrnlinfo-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Kernel Debug Information, R0 Driver, Darwin.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#ifdef IN_RING0
+# include "the-darwin-kernel.h"
+# include <sys/kauth.h>
+RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */
+# if MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+# include <sys/kpi_mbuf.h>
+# include <net/kpi_interfacefilter.h>
+# include <sys/kpi_socket.h>
+# endif
+# include <sys/kpi_socketfilter.h>
+RT_C_DECLS_END
+# include <sys/buf.h>
+# include <sys/vm.h>
+# include <sys/vnode_if.h>
+/*# include <sys/sysctl.h>*/
+# include <sys/systm.h>
+# include <vfs/vfs_support.h>
+/*# include <miscfs/specfs/specdev.h>*/
+#else
+# include <stdio.h> /* for printf */
+#endif
+
+#if !defined(IN_RING0) && !defined(DOXYGEN_RUNNING) /* A linking tweak for the testcase: */
+# include <iprt/cdefs.h>
+# undef RTR0DECL
+# define RTR0DECL(type) DECLHIDDEN(type) RTCALL
+#endif
+
+#include "internal/iprt.h"
+#include <iprt/dbg.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/formats/mach-o.h>
+#include "internal/magics.h"
+
+/** @def MY_CPU_TYPE
+ * The CPU type targeted by the compiler. */
+/** @def MY_CPU_TYPE
+ * The "ALL" CPU subtype targeted by the compiler. */
+/** @def MY_MACHO_HEADER
+ * The Mach-O header targeted by the compiler. */
+/** @def MY_MACHO_MAGIC
+ * The Mach-O header magic we're targeting. */
+/** @def MY_SEGMENT_COMMAND
+ * The segment command targeted by the compiler. */
+/** @def MY_SECTION
+ * The section struture targeted by the compiler. */
+/** @def MY_NLIST
+ * The symbol table entry targeted by the compiler. */
+#ifdef RT_ARCH_X86
+# define MY_CPU_TYPE CPU_TYPE_I386
+# define MY_CPU_SUBTYPE_ALL CPU_SUBTYPE_I386_ALL
+# define MY_MACHO_HEADER mach_header_32_t
+# define MY_MACHO_MAGIC IMAGE_MACHO32_SIGNATURE
+# define MY_SEGMENT_COMMAND segment_command_32_t
+# define MY_SECTION section_32_t
+# define MY_NLIST macho_nlist_32_t
+
+#elif defined(RT_ARCH_AMD64)
+# define MY_CPU_TYPE CPU_TYPE_X86_64
+# define MY_CPU_SUBTYPE_ALL CPU_SUBTYPE_X86_64_ALL
+# define MY_MACHO_HEADER mach_header_64_t
+# define MY_MACHO_MAGIC IMAGE_MACHO64_SIGNATURE
+# define MY_SEGMENT_COMMAND segment_command_64_t
+# define MY_SECTION section_64_t
+# define MY_NLIST macho_nlist_64_t
+
+#elif defined(RT_ARCH_ARM64)
+# define MY_CPU_TYPE CPU_TYPE_ARM64
+# define MY_CPU_SUBTYPE_ALL CPU_SUBTYPE_ARM64_ALL
+# define MY_MACHO_HEADER mach_header_64_t
+# define MY_MACHO_MAGIC IMAGE_MACHO64_SIGNATURE
+# define MY_SEGMENT_COMMAND segment_command_64_t
+# define MY_SECTION section_64_t
+# define MY_NLIST macho_nlist_64_t
+
+#else
+# error "Port me!"
+#endif
+
+/** @name Return macros for make it simpler to track down too paranoid code.
+ * @{
+ */
+#ifdef DEBUG
+# define RETURN_VERR_BAD_EXE_FORMAT \
+ do { Assert(!g_fBreakpointOnError); return VERR_BAD_EXE_FORMAT; } while (0)
+# define RETURN_VERR_LDR_UNEXPECTED \
+ do { Assert(!g_fBreakpointOnError); return VERR_LDR_UNEXPECTED; } while (0)
+# define RETURN_VERR_LDR_ARCH_MISMATCH \
+ do { Assert(!g_fBreakpointOnError); return VERR_LDR_ARCH_MISMATCH; } while (0)
+#else
+# define RETURN_VERR_BAD_EXE_FORMAT do { return VERR_BAD_EXE_FORMAT; } while (0)
+# define RETURN_VERR_LDR_UNEXPECTED do { return VERR_LDR_UNEXPECTED; } while (0)
+# define RETURN_VERR_LDR_ARCH_MISMATCH do { return VERR_LDR_ARCH_MISMATCH; } while (0)
+#endif
+#if defined(DEBUG_bird) && !defined(IN_RING3)
+# define LOG_MISMATCH(...) kprintf(__VA_ARGS__)
+# define LOG_NOT_PRESENT(...) kprintf(__VA_ARGS__)
+# define LOG_BAD_SYM(...) kprintf(__VA_ARGS__)
+# define LOG_SUCCESS(...) kprintf(__VA_ARGS__)
+#else
+# define LOG_MISMATCH(...) Log((__VA_ARGS__))
+# define LOG_NOT_PRESENT(...) Log((__VA_ARGS__))
+# define LOG_BAD_SYM(...) printf(__VA_ARGS__)
+# define LOG_SUCCESS(...) printf(__VA_ARGS__)
+#endif
+/** @} */
+
+#define VERR_LDR_UNEXPECTED (-641)
+
+#ifndef RT_OS_DARWIN
+# define MAC_OS_X_VERSION_MIN_REQUIRED 1050
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Our internal representation of the mach_kernel after loading it's symbols
+ * and successfully resolving their addresses.
+ */
+typedef struct RTDBGKRNLINFOINT
+{
+ /** Magic value (RTDBGKRNLINFO_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+
+ /** Set if this is an in-memory rather than on-disk instance. */
+ bool fIsInMem;
+ bool afAlignment[7];
+
+ /** @name Result.
+ * @{ */
+ /** Pointer to the string table. */
+ char *pachStrTab;
+ /** The size of the string table. */
+ uint32_t cbStrTab;
+ /** The file offset of the string table. */
+ uint32_t offStrTab;
+ /** The link address of the string table. */
+ uintptr_t uStrTabLinkAddr;
+ /** Pointer to the symbol table. */
+ MY_NLIST *paSyms;
+ /** The size of the symbol table. */
+ uint32_t cSyms;
+ /** The file offset of the symbol table. */
+ uint32_t offSyms;
+ /** The link address of the symbol table. */
+ uintptr_t uSymTabLinkAddr;
+ /** The link address of the text segment. */
+ uintptr_t uTextSegLinkAddr;
+ /** Size of the text segment. */
+ uintptr_t cbTextSeg;
+ /** Offset between link address and actual load address of the text segment. */
+ uintptr_t offLoad;
+ /** The minimum OS version (A.B.C; A is 16 bits, B & C each 8 bits). */
+ uint32_t uMinOsVer;
+ /** The SDK version (A.B.C; A is 16 bits, B & C each 8 bits). */
+ uint32_t uSdkVer;
+ /** The source version (A.B.C.D.E; A is 24 bits, the rest 10 each). */
+ uint64_t uSrcVer;
+ /** @} */
+
+ /** @name Used during loading.
+ * @{ */
+ /** The file handle. */
+ RTFILE hFile;
+ /** The architecture image offset (fat_arch_t::offset). */
+ uint64_t offArch;
+ /** The architecture image size (fat_arch_t::size). */
+ uint32_t cbArch;
+ /** The number of load commands (mach_header_XX_t::ncmds). */
+ uint32_t cLoadCmds;
+ /** The size of the load commands. */
+ uint32_t cbLoadCmds;
+ /** The load commands. */
+ load_command_t *pLoadCmds;
+ /** The number of segments. */
+ uint32_t cSegments;
+ /** The number of sections. */
+ uint32_t cSections;
+ /** Section pointer table (points into the load commands). */
+ MY_SEGMENT_COMMAND const *apSegments[MACHO_MAX_SECT / 2];
+ /** Load displacement table for each segment. */
+ uintptr_t aoffLoadSegments[MACHO_MAX_SECT / 2];
+ /** Section pointer table (points into the load commands). */
+ MY_SECTION const *apSections[MACHO_MAX_SECT];
+ /** Mapping table to quickly get to a segment from MY_NLIST::n_sect. */
+ uint8_t auSections2Segment[MACHO_MAX_SECT];
+ /** @} */
+
+ /** Buffer space. */
+ char abBuf[_4K];
+} RTDBGKRNLINFOINT;
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#ifdef DEBUG
+static bool g_fBreakpointOnError = false;
+#endif
+
+
+/**
+ * Close and free up resources we no longer needs.
+ *
+ * @param pThis The internal scratch data.
+ */
+static void rtR0DbgKrnlDarwinLoadDone(RTDBGKRNLINFOINT *pThis)
+{
+ if (!pThis->fIsInMem)
+ RTFileClose(pThis->hFile);
+ pThis->hFile = NIL_RTFILE;
+
+ if (!pThis->fIsInMem)
+ RTMemFree(pThis->pLoadCmds);
+ pThis->pLoadCmds = NULL;
+ RT_ZERO(pThis->apSections);
+ RT_ZERO(pThis->apSegments);
+}
+
+
+/**
+ * Looks up a kernel symbol record.
+ *
+ * @returns Pointer to the symbol record or NULL if not found.
+ * @param pThis The internal scratch data.
+ * @param pszSymbol The symbol to resolve. Automatically prefixed
+ * with an underscore.
+ */
+static MY_NLIST const *rtR0DbgKrnlDarwinLookupSym(RTDBGKRNLINFOINT *pThis, const char *pszSymbol)
+{
+ uint32_t const cSyms = pThis->cSyms;
+ MY_NLIST const *pSym = pThis->paSyms;
+
+#if 1
+ /* linear search. */
+ for (uint32_t iSym = 0; iSym < cSyms; iSym++, pSym++)
+ {
+ if (pSym->n_type & MACHO_N_STAB)
+ continue;
+
+ const char *pszTabName= &pThis->pachStrTab[(uint32_t)pSym->n_un.n_strx];
+ if ( *pszTabName == '_'
+ && strcmp(pszTabName + 1, pszSymbol) == 0)
+ return pSym;
+ }
+#else
+ /** @todo binary search. */
+#endif
+
+ return NULL;
+}
+
+
+/**
+ * Looks up a kernel symbol.
+ *
+ * @returns The symbol address on success, 0 on failure.
+ * @param pThis The internal scratch data.
+ * @param pszSymbol The symbol to resolve. Automatically prefixed
+ * with an underscore.
+ */
+static uintptr_t rtR0DbgKrnlDarwinLookup(RTDBGKRNLINFOINT *pThis, const char *pszSymbol)
+{
+ MY_NLIST const *pSym = rtR0DbgKrnlDarwinLookupSym(pThis, pszSymbol);
+ if (pSym)
+ {
+ uint8_t idxSeg = pThis->auSections2Segment[pSym->n_sect];
+ if (pThis->aoffLoadSegments[idxSeg] != UINTPTR_MAX)
+ return pSym->n_value + pThis->aoffLoadSegments[idxSeg];
+ }
+
+ return 0;
+}
+
+
+/* Rainy day: Find the right headers for these symbols ... if there are any. */
+extern "C" void ev_try_lock(void);
+extern "C" void OSMalloc(void);
+extern "C" void OSlibkernInit(void);
+extern "C" void kdp_set_interface(void);
+
+
+/*
+ * Determine the load displacement (10.8 kernels are PIE).
+ *
+ * Starting with 11.0 (BigSur) all segments can have different load displacements
+ * so determine the displacements from known symbols.
+ *
+ * @returns IPRT status code
+ * @param pThis The internal scratch data.
+ */
+static int rtR0DbgKrnlDarwinInitLoadDisplacements(RTDBGKRNLINFOINT *pThis)
+{
+ static struct
+ {
+ const char *pszName;
+ uintptr_t uAddr;
+ } const s_aStandardSyms[] =
+ {
+#ifdef IN_RING0
+# define KNOWN_ENTRY(a_Sym) { #a_Sym, (uintptr_t)&a_Sym }
+#else
+# define KNOWN_ENTRY(a_Sym) { #a_Sym, 0 }
+#endif
+ KNOWN_ENTRY(vm_map_unwire), /* __TEXT */
+ KNOWN_ENTRY(kernel_map), /* __HIB */
+ KNOWN_ENTRY(gIOServicePlane), /* __DATA (__HIB on ElCapitan) */
+ KNOWN_ENTRY(page_mask) /* __DATA on ElCapitan */
+#undef KNOWN_ENTRY
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aStandardSyms); i++)
+ {
+ MY_NLIST const *pSym = rtR0DbgKrnlDarwinLookupSym(pThis, s_aStandardSyms[i].pszName);
+ if (RT_UNLIKELY(!pSym))
+ return VERR_INTERNAL_ERROR_2;
+
+ uint8_t idxSeg = pThis->auSections2Segment[pSym->n_sect];
+#ifdef IN_RING0
+ /*
+ * The segment should either not have the load displacement determined or it should
+ * be the same for all symbols in the same segment.
+ */
+ if ( pThis->aoffLoadSegments[idxSeg] != UINTPTR_MAX
+ && pThis->aoffLoadSegments[idxSeg] != s_aStandardSyms[i].uAddr - pSym->n_value)
+ return VERR_INTERNAL_ERROR_2;
+
+ pThis->aoffLoadSegments[idxSeg] = s_aStandardSyms[i].uAddr - pSym->n_value;
+#elif defined(IN_RING3)
+ pThis->aoffLoadSegments[idxSeg] = 0;
+#else
+# error "Either IN_RING0 or IN_RING3 msut be defined"
+#endif
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Check the symbol table against symbols we known symbols.
+ *
+ * This is done to detect whether the on disk image and the in
+ * memory images matches. Mismatches could stem from user
+ * replacing the default kernel image on disk.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal scratch data.
+ * @param pszKernelFile The name of the kernel file.
+ */
+static int rtR0DbgKrnlDarwinCheckStandardSymbols(RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
+{
+ static struct
+ {
+ const char *pszName;
+ uintptr_t uAddr;
+ } const s_aStandardCandles[] =
+ {
+#ifdef IN_RING0
+# define KNOWN_ENTRY(a_Sym) { #a_Sym, (uintptr_t)&a_Sym }
+#else
+# define KNOWN_ENTRY(a_Sym) { #a_Sym, 0 }
+#endif
+ /* IOKit: */
+ KNOWN_ENTRY(IOMalloc),
+ KNOWN_ENTRY(IOFree),
+ KNOWN_ENTRY(IOSleep),
+ KNOWN_ENTRY(IORWLockAlloc),
+ KNOWN_ENTRY(IORecursiveLockLock),
+ KNOWN_ENTRY(IOSimpleLockAlloc),
+ KNOWN_ENTRY(PE_cpu_halt),
+ KNOWN_ENTRY(gIOKitDebug),
+ KNOWN_ENTRY(gIOServicePlane),
+ KNOWN_ENTRY(ev_try_lock),
+
+ /* Libkern: */
+ KNOWN_ENTRY(OSAddAtomic),
+ KNOWN_ENTRY(OSBitAndAtomic),
+ KNOWN_ENTRY(OSBitOrAtomic),
+ KNOWN_ENTRY(OSBitXorAtomic),
+ KNOWN_ENTRY(OSCompareAndSwap),
+ KNOWN_ENTRY(OSMalloc),
+ KNOWN_ENTRY(OSlibkernInit),
+ KNOWN_ENTRY(bcmp),
+ KNOWN_ENTRY(copyout),
+ KNOWN_ENTRY(copyin),
+ KNOWN_ENTRY(kprintf),
+ KNOWN_ENTRY(printf),
+ KNOWN_ENTRY(lck_grp_alloc_init),
+ KNOWN_ENTRY(lck_mtx_alloc_init),
+ KNOWN_ENTRY(lck_rw_alloc_init),
+ KNOWN_ENTRY(lck_spin_alloc_init),
+ KNOWN_ENTRY(osrelease),
+ KNOWN_ENTRY(ostype),
+ KNOWN_ENTRY(panic),
+ KNOWN_ENTRY(strprefix),
+ //KNOWN_ENTRY(sysctlbyname), - we get kernel_sysctlbyname from the 10.10+ kernels.
+ KNOWN_ENTRY(vsscanf),
+ KNOWN_ENTRY(page_mask),
+
+ /* Mach: */
+ KNOWN_ENTRY(absolutetime_to_nanoseconds),
+ KNOWN_ENTRY(assert_wait),
+ KNOWN_ENTRY(clock_delay_until),
+ KNOWN_ENTRY(clock_get_uptime),
+ KNOWN_ENTRY(current_task),
+ KNOWN_ENTRY(current_thread),
+ KNOWN_ENTRY(kernel_task),
+ KNOWN_ENTRY(lck_mtx_sleep),
+ KNOWN_ENTRY(lck_rw_sleep),
+ KNOWN_ENTRY(lck_spin_sleep),
+ KNOWN_ENTRY(mach_absolute_time),
+ KNOWN_ENTRY(semaphore_create),
+ KNOWN_ENTRY(task_reference),
+ KNOWN_ENTRY(thread_block),
+ KNOWN_ENTRY(thread_reference),
+ KNOWN_ENTRY(thread_terminate),
+ KNOWN_ENTRY(thread_wakeup_prim),
+
+ /* BSDKernel: */
+ KNOWN_ENTRY(buf_size),
+ KNOWN_ENTRY(copystr),
+ KNOWN_ENTRY(current_proc),
+ KNOWN_ENTRY(kauth_getuid),
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ KNOWN_ENTRY(kauth_cred_unref),
+#else
+ KNOWN_ENTRY(kauth_cred_rele),
+#endif
+ KNOWN_ENTRY(msleep),
+ KNOWN_ENTRY(nanotime),
+ KNOWN_ENTRY(nop_close),
+ KNOWN_ENTRY(proc_pid),
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101500
+ KNOWN_ENTRY(mbuf_data),
+ KNOWN_ENTRY(ifnet_hdrlen),
+ KNOWN_ENTRY(ifnet_set_promiscuous),
+ KNOWN_ENTRY(sock_accept),
+ KNOWN_ENTRY(sockopt_name),
+#endif
+ //KNOWN_ENTRY(spec_write),
+ KNOWN_ENTRY(suword),
+ //KNOWN_ENTRY(sysctl_int),
+ KNOWN_ENTRY(uio_rw),
+ KNOWN_ENTRY(vfs_flags),
+ KNOWN_ENTRY(vfs_name),
+ KNOWN_ENTRY(vfs_statfs),
+ KNOWN_ENTRY(VNOP_READ),
+ KNOWN_ENTRY(uio_create),
+ KNOWN_ENTRY(uio_addiov),
+ KNOWN_ENTRY(uio_free),
+ KNOWN_ENTRY(vnode_get),
+ KNOWN_ENTRY(vnode_open),
+ KNOWN_ENTRY(vnode_ref),
+ KNOWN_ENTRY(vnode_rele),
+ KNOWN_ENTRY(vnop_close_desc),
+ KNOWN_ENTRY(wakeup),
+ KNOWN_ENTRY(wakeup_one),
+
+ /* Unsupported: */
+ KNOWN_ENTRY(kdp_set_interface),
+ KNOWN_ENTRY(pmap_find_phys),
+ KNOWN_ENTRY(vm_map),
+ KNOWN_ENTRY(vm_protect),
+ KNOWN_ENTRY(vm_region),
+ KNOWN_ENTRY(vm_map_unwire), /* vm_map_wire has an alternative symbol, vm_map_wire_external, in 10.11 */
+ KNOWN_ENTRY(PE_kputc),
+ KNOWN_ENTRY(kernel_map),
+ KNOWN_ENTRY(kernel_pmap),
+#undef KNOWN_ENTRY
+ };
+
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aStandardCandles); i++)
+ {
+ uintptr_t uAddr = rtR0DbgKrnlDarwinLookup(pThis, s_aStandardCandles[i].pszName);
+#ifdef IN_RING0
+ if (uAddr != s_aStandardCandles[i].uAddr)
+#else
+ if (uAddr == 0)
+#endif
+ {
+#if defined(IN_RING0) && defined(DEBUG_bird)
+ kprintf("RTR0DbgKrnlInfoOpen: error: %s (%p != %p) in %s\n",
+ s_aStandardCandles[i].pszName, (void *)uAddr, (void *)s_aStandardCandles[i].uAddr, pszKernelFile);
+#endif
+ printf("RTR0DbgKrnlInfoOpen: error: %s (%p != %p) in %s\n",
+ s_aStandardCandles[i].pszName, (void *)uAddr, (void *)s_aStandardCandles[i].uAddr, pszKernelFile);
+ return VERR_INTERNAL_ERROR_2;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads and validates the symbol and string tables.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal scratch data.
+ * @param pszKernelFile The name of the kernel file.
+ */
+static int rtR0DbgKrnlDarwinParseSymTab(RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
+{
+ /*
+ * The first string table symbol must be a zero length name.
+ */
+ if (pThis->pachStrTab[0] != '\0')
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ /*
+ * Validate the symbol table.
+ */
+ const char *pszPrev = "";
+ uint32_t const cSyms = pThis->cSyms;
+ MY_NLIST const *pSym = pThis->paSyms;
+ for (uint32_t iSym = 0; iSym < cSyms; iSym++, pSym++)
+ {
+ if ((uint32_t)pSym->n_un.n_strx >= pThis->cbStrTab)
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u has a bad string table index: %#x vs cbStrTab=%#x\n",
+ pszKernelFile, iSym, pSym->n_un.n_strx, pThis->cbStrTab);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ const char *pszSym = &pThis->pachStrTab[(uint32_t)pSym->n_un.n_strx];
+#ifdef IN_RING3
+ RTAssertMsg2("%05i: %02x:%08llx %02x %04x %s\n", iSym, pSym->n_sect, (uint64_t)pSym->n_value, pSym->n_type, pSym->n_desc, pszSym);
+#endif
+
+ if (strcmp(pszSym, pszPrev) < 0)
+ RETURN_VERR_BAD_EXE_FORMAT; /* not sorted */
+
+ if (!(pSym->n_type & MACHO_N_STAB))
+ {
+ switch (pSym->n_type & MACHO_N_TYPE)
+ {
+ case MACHO_N_SECT:
+ if (pSym->n_sect == MACHO_NO_SECT)
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_sect = MACHO_NO_SECT\n",
+ pszKernelFile, iSym, pszSym);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ if (pSym->n_sect > pThis->cSections)
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_sect (%u) is higher than cSections (%u)\n",
+ pszKernelFile, iSym, pszSym, pSym->n_sect, pThis->cSections);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ if (pSym->n_desc & ~(REFERENCED_DYNAMICALLY | N_WEAK_DEF))
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: Unexpected value n_desc=%#x\n",
+ pszKernelFile, iSym, pszSym, pSym->n_desc);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ if ( pSym->n_value < pThis->apSections[pSym->n_sect - 1]->addr
+ && strcmp(pszSym, "__mh_execute_header")) /* in 10.8 it's no longer absolute (PIE?). */
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_value (%#llx) < section addr (%#llx)\n",
+ pszKernelFile, iSym, pszSym, (uint64_t)pSym->n_value,
+ (uint64_t)pThis->apSections[pSym->n_sect - 1]->addr);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ if ( pSym->n_value - pThis->apSections[pSym->n_sect - 1]->addr
+ > pThis->apSections[pSym->n_sect - 1]->size
+ && strcmp(pszSym, "__mh_execute_header")) /* see above. */
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Symbol #%u '%s' problem: n_value (%#llx) >= end of section (%#llx + %#llx)\n",
+ pszKernelFile, iSym, pszSym, (uint64_t)pSym->n_value,
+ (uint64_t)pThis->apSections[pSym->n_sect - 1]->addr,
+ (uint64_t)pThis->apSections[pSym->n_sect - 1]->size);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ break;
+
+ case MACHO_N_ABS:
+ if ( pSym->n_sect != MACHO_NO_SECT
+ && ( strcmp(pszSym, "__mh_execute_header") /* n_sect=1 in 10.7/amd64 */
+ || pSym->n_sect > pThis->cSections) )
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Abs symbol #%u '%s' problem: n_sect (%u) is not MACHO_NO_SECT (cSections is %u)\n",
+ pszKernelFile, iSym, pszSym, pSym->n_sect, pThis->cSections);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ if (pSym->n_desc & ~(REFERENCED_DYNAMICALLY | N_WEAK_DEF))
+ {
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Abs symbol #%u '%s' problem: Unexpected value n_desc=%#x\n",
+ pszKernelFile, iSym, pszSym, pSym->n_desc);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ break;
+
+ case MACHO_N_UNDF:
+ /* No undefined or common symbols in the kernel. */
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected undefined symbol #%u '%s'\n", pszKernelFile, iSym, pszSym);
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ case MACHO_N_INDR:
+ /* No indirect symbols in the kernel. */
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected indirect symbol #%u '%s'\n", pszKernelFile, iSym, pszSym);
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ case MACHO_N_PBUD:
+ /* No prebound symbols in the kernel. */
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected prebound symbol #%u '%s'\n", pszKernelFile, iSym, pszSym);
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ default:
+ LOG_BAD_SYM("RTR0DbgKrnlInfoOpen: %s: Unexpected symbol n_type %#x for symbol #%u '%s'\n",
+ pszKernelFile, pSym->n_type, iSym, pszSym);
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ }
+ /* else: Ignore debug symbols. */
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Uses the segment table to translate a file offset into a virtual memory
+ * address.
+ *
+ * @returns The virtual memory address on success, 0 if not found.
+ * @param pThis The instance.
+ * @param offFile The file offset to translate.
+ */
+static uintptr_t rtR0DbgKrnlDarwinFileOffToVirtAddr(RTDBGKRNLINFOINT *pThis, uint64_t offFile)
+{
+ uint32_t iSeg = pThis->cSegments;
+ while (iSeg-- > 0)
+ {
+ uint64_t offSeg = offFile - pThis->apSegments[iSeg]->fileoff;
+ if (offSeg < pThis->apSegments[iSeg]->vmsize)
+ return pThis->apSegments[iSeg]->vmaddr + (uintptr_t)offSeg;
+ }
+ return 0;
+}
+
+
+/**
+ * Parses and validates the load commands.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal scratch data.
+ */
+static int rtR0DbgKrnlDarwinParseCommands(RTDBGKRNLINFOINT *pThis)
+{
+ Assert(pThis->pLoadCmds);
+
+ /*
+ * Reset the state.
+ */
+ pThis->offStrTab = 0;
+ pThis->cbStrTab = 0;
+ pThis->offSyms = 0;
+ pThis->cSyms = 0;
+ pThis->cSections = 0;
+ pThis->uTextSegLinkAddr = 0;
+ pThis->cbTextSeg = 0;
+ pThis->uMinOsVer = 0;
+ pThis->uSdkVer = 0;
+ pThis->uSrcVer = 0;
+
+ /*
+ * Validate the relevant commands, picking up sections and the symbol
+ * table location.
+ */
+ load_command_t const *pCmd = pThis->pLoadCmds;
+ for (uint32_t iCmd = 0; ; iCmd++)
+ {
+ /* cmd index & offset. */
+ uintptr_t offCmd = (uintptr_t)pCmd - (uintptr_t)pThis->pLoadCmds;
+ if (offCmd == pThis->cbLoadCmds && iCmd == pThis->cLoadCmds)
+ break;
+ if (offCmd + sizeof(*pCmd) > pThis->cbLoadCmds)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (iCmd >= pThis->cLoadCmds)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ /* cmdsize */
+ if (pCmd->cmdsize < sizeof(*pCmd))
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (pCmd->cmdsize > pThis->cbLoadCmds)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (RT_ALIGN_32(pCmd->cmdsize, 4) != pCmd->cmdsize)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ /* cmd */
+ switch (pCmd->cmd & ~LC_REQ_DYLD)
+ {
+ /* Validate and store the symbol table details. */
+ case LC_SYMTAB:
+ {
+ struct symtab_command const *pSymTab = (struct symtab_command const *)pCmd;
+ if (pSymTab->cmdsize != sizeof(*pSymTab))
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (pSymTab->nsyms > _1M)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (pSymTab->strsize > _2M)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ pThis->offStrTab = pSymTab->stroff;
+ pThis->cbStrTab = pSymTab->strsize;
+ pThis->offSyms = pSymTab->symoff;
+ pThis->cSyms = pSymTab->nsyms;
+ break;
+ }
+
+ /* Validate the segment. */
+#if ARCH_BITS == 32
+ case LC_SEGMENT_32:
+#elif ARCH_BITS == 64
+ case LC_SEGMENT_64:
+#else
+# error ARCH_BITS
+#endif
+ {
+ MY_SEGMENT_COMMAND const *pSeg = (MY_SEGMENT_COMMAND const *)pCmd;
+ if (pSeg->cmdsize < sizeof(*pSeg))
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ if (pSeg->segname[0] == '\0')
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ if (pSeg->nsects > MACHO_MAX_SECT)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (pSeg->nsects * sizeof(MY_SECTION) + sizeof(*pSeg) != pSeg->cmdsize)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ if (pSeg->flags & ~(SG_HIGHVM | SG_FVMLIB | SG_NORELOC | SG_PROTECTED_VERSION_1))
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ if ( pSeg->vmaddr != 0
+ || !strcmp(pSeg->segname, "__PAGEZERO"))
+ {
+ if (pSeg->vmaddr + RT_ALIGN_Z(pSeg->vmsize, RT_BIT_32(12)) < pSeg->vmaddr)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+ else if (pSeg->vmsize)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ if (pSeg->maxprot & ~VM_PROT_ALL)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (pSeg->initprot & ~VM_PROT_ALL)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ /* Validate the sections. */
+ uint32_t uAlignment = 0;
+ MY_SECTION const *paSects = (MY_SECTION const *)(pSeg + 1);
+ for (uint32_t i = 0; i < pSeg->nsects; i++)
+ {
+ if (paSects[i].sectname[0] == '\0')
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (memcmp(paSects[i].segname, pSeg->segname, sizeof(pSeg->segname)))
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ switch (paSects[i].flags & SECTION_TYPE)
+ {
+ case S_REGULAR:
+ case S_CSTRING_LITERALS:
+ case S_NON_LAZY_SYMBOL_POINTERS:
+ case S_MOD_INIT_FUNC_POINTERS:
+ case S_MOD_TERM_FUNC_POINTERS:
+ case S_COALESCED:
+ case S_4BYTE_LITERALS:
+ if ( pSeg->filesize != 0
+ ? paSects[i].offset - pSeg->fileoff >= pSeg->filesize
+ : paSects[i].offset - pSeg->fileoff != pSeg->filesize)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if ( paSects[i].addr != 0
+ && paSects[i].offset - pSeg->fileoff != paSects[i].addr - pSeg->vmaddr)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ break;
+
+ case S_ZEROFILL:
+ if (paSects[i].offset != 0)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ break;
+
+ /* not observed */
+ case S_SYMBOL_STUBS:
+ case S_INTERPOSING:
+ case S_8BYTE_LITERALS:
+ case S_16BYTE_LITERALS:
+ case S_DTRACE_DOF:
+ case S_LAZY_SYMBOL_POINTERS:
+ case S_LAZY_DYLIB_SYMBOL_POINTERS:
+ RETURN_VERR_LDR_UNEXPECTED;
+ case S_GB_ZEROFILL:
+ RETURN_VERR_LDR_UNEXPECTED;
+ default:
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+
+ if (paSects[i].align > 12)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (paSects[i].align > uAlignment)
+ uAlignment = paSects[i].align;
+
+ /* Add to the section table. */
+ if (pThis->cSections >= RT_ELEMENTS(pThis->apSections))
+ RETURN_VERR_BAD_EXE_FORMAT;
+ pThis->auSections2Segment[pThis->cSections] = pThis->cSegments;
+ pThis->apSections[pThis->cSections++] = &paSects[i];
+ }
+
+ if (RT_ALIGN_Z(pSeg->vmaddr, RT_BIT_32(uAlignment)) != pSeg->vmaddr)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if ( pSeg->filesize > RT_ALIGN_Z(pSeg->vmsize, RT_BIT_32(uAlignment))
+ && pSeg->vmsize != 0)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ /*
+ * Add to the segment table.
+ */
+ if (pThis->cSegments >= RT_ELEMENTS(pThis->apSegments))
+ RETURN_VERR_BAD_EXE_FORMAT;
+ pThis->apSegments[pThis->cSegments++] = pSeg;
+
+ /*
+ * Take down the text segment size and link address (for in-mem variant):
+ */
+ if (!strcmp(pSeg->segname, "__TEXT"))
+ {
+ if (pThis->cbTextSeg != 0)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ pThis->uTextSegLinkAddr = pSeg->vmaddr;
+ pThis->cbTextSeg = pSeg->vmsize;
+ }
+ break;
+ }
+
+ case LC_UUID:
+ if (pCmd->cmdsize != sizeof(uuid_command))
+ RETURN_VERR_BAD_EXE_FORMAT;
+ break;
+
+ case LC_DYSYMTAB:
+ case LC_UNIXTHREAD:
+ case LC_CODE_SIGNATURE:
+ case LC_VERSION_MIN_MACOSX:
+ case LC_FUNCTION_STARTS:
+ case LC_MAIN:
+ case LC_DATA_IN_CODE:
+ case LC_ENCRYPTION_INFO_64:
+ case LC_LINKER_OPTION:
+ case LC_LINKER_OPTIMIZATION_HINT:
+ case LC_VERSION_MIN_TVOS:
+ case LC_VERSION_MIN_WATCHOS:
+ case LC_NOTE:
+ case LC_SEGMENT_SPLIT_INFO:
+ break;
+
+ case LC_BUILD_VERSION:
+ if (pCmd->cmdsize >= RT_UOFFSETOF(build_version_command_t, aTools))
+ {
+ build_version_command_t *pBldVerCmd = (build_version_command_t *)pCmd;
+ pThis->uMinOsVer = pBldVerCmd->minos;
+ pThis->uSdkVer = pBldVerCmd->sdk;
+ }
+ break;
+
+ case LC_SOURCE_VERSION:
+ if (pCmd->cmdsize == sizeof(source_version_command_t))
+ {
+ source_version_command_t *pSrcVerCmd = (source_version_command_t *)pCmd;
+ pThis->uSrcVer = pSrcVerCmd->version;
+ }
+ break;
+
+ /* not observed */
+ case LC_SYMSEG:
+#if ARCH_BITS == 32
+ case LC_SEGMENT_64:
+#elif ARCH_BITS == 64
+ case LC_SEGMENT_32:
+#endif
+ case LC_ROUTINES_64:
+ case LC_ROUTINES:
+ case LC_THREAD:
+ case LC_LOADFVMLIB:
+ case LC_IDFVMLIB:
+ case LC_IDENT:
+ case LC_FVMFILE:
+ case LC_PREPAGE:
+ case LC_TWOLEVEL_HINTS:
+ case LC_PREBIND_CKSUM:
+ case LC_ENCRYPTION_INFO:
+ RETURN_VERR_LDR_UNEXPECTED;
+
+ /* no phones here yet */
+ case LC_VERSION_MIN_IPHONEOS:
+ RETURN_VERR_LDR_UNEXPECTED;
+
+ /* dylib */
+ case LC_LOAD_DYLIB:
+ case LC_ID_DYLIB:
+ case LC_LOAD_DYLINKER:
+ case LC_ID_DYLINKER:
+ case LC_PREBOUND_DYLIB:
+ case LC_LOAD_WEAK_DYLIB & ~LC_REQ_DYLD:
+ case LC_SUB_FRAMEWORK:
+ case LC_SUB_UMBRELLA:
+ case LC_SUB_CLIENT:
+ case LC_SUB_LIBRARY:
+ case LC_RPATH:
+ case LC_REEXPORT_DYLIB:
+ case LC_LAZY_LOAD_DYLIB:
+ case LC_DYLD_INFO:
+ case LC_DYLD_INFO_ONLY:
+ case LC_LOAD_UPWARD_DYLIB:
+ case LC_DYLD_ENVIRONMENT:
+ case LC_DYLIB_CODE_SIGN_DRS:
+ RETURN_VERR_LDR_UNEXPECTED;
+
+ default:
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+
+ /* next */
+ pCmd = (load_command_t *)((uintptr_t)pCmd + pCmd->cmdsize);
+ }
+
+ /*
+ * Try figure out the virtual addresses for the symbol and string tables.
+ */
+ if (pThis->cbStrTab > 0)
+ pThis->uStrTabLinkAddr = rtR0DbgKrnlDarwinFileOffToVirtAddr(pThis, pThis->offStrTab);
+ if (pThis->cSyms > 0)
+ pThis->uSymTabLinkAddr = rtR0DbgKrnlDarwinFileOffToVirtAddr(pThis, pThis->offSyms);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Loads and validates the symbol and string tables.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal scratch data.
+ * @param pszKernelFile The name of the kernel file.
+ */
+static int rtR0DbgKrnlDarwinLoadSymTab(RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
+{
+ /*
+ * Load the tables.
+ */
+ int rc;
+ pThis->paSyms = (MY_NLIST *)RTMemAllocZ(pThis->cSyms * sizeof(MY_NLIST));
+ if (pThis->paSyms)
+ {
+ rc = RTFileReadAt(pThis->hFile, pThis->offArch + pThis->offSyms, pThis->paSyms, pThis->cSyms * sizeof(MY_NLIST), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pachStrTab = (char *)RTMemAllocZ(pThis->cbStrTab + 1);
+ if (pThis->pachStrTab)
+ {
+ rc = RTFileReadAt(pThis->hFile, pThis->offArch + pThis->offStrTab, pThis->pachStrTab, pThis->cbStrTab, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Join paths with the in-memory code path.
+ */
+ rc = rtR0DbgKrnlDarwinParseSymTab(pThis, pszKernelFile);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Loads the load commands and validates them.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal scratch data.
+ */
+static int rtR0DbgKrnlDarwinLoadCommands(RTDBGKRNLINFOINT *pThis)
+{
+ int rc;
+ pThis->pLoadCmds = (load_command_t *)RTMemAlloc(pThis->cbLoadCmds);
+ if (pThis->pLoadCmds)
+ {
+ rc = RTFileReadAt(pThis->hFile, pThis->offArch + sizeof(MY_MACHO_HEADER), pThis->pLoadCmds, pThis->cbLoadCmds, NULL);
+ if (RT_SUCCESS(rc))
+ rc = rtR0DbgKrnlDarwinParseCommands(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+/**
+ * Loads the FAT and MACHO headers, noting down the relevant info.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal scratch data.
+ */
+static int rtR0DbgKrnlDarwinLoadFileHeaders(RTDBGKRNLINFOINT *pThis)
+{
+ uint32_t i;
+
+ pThis->offArch = 0;
+ pThis->cbArch = 0;
+
+ /*
+ * Read the first bit of the file, parse the FAT if found there.
+ */
+ int rc = RTFileReadAt(pThis->hFile, 0, pThis->abBuf, sizeof(fat_header_t) + sizeof(fat_arch_t) * 16, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ fat_header_t *pFat = (fat_header *)pThis->abBuf;
+ fat_arch_t *paFatArches = (fat_arch_t *)(pFat + 1);
+
+ /* Correct FAT endian first. */
+ if (pFat->magic == IMAGE_FAT_SIGNATURE_OE)
+ {
+ pFat->magic = RT_BSWAP_U32(pFat->magic);
+ pFat->nfat_arch = RT_BSWAP_U32(pFat->nfat_arch);
+ i = RT_MIN(pFat->nfat_arch, 16);
+ while (i-- > 0)
+ {
+ paFatArches[i].cputype = RT_BSWAP_U32(paFatArches[i].cputype);
+ paFatArches[i].cpusubtype = RT_BSWAP_U32(paFatArches[i].cpusubtype);
+ paFatArches[i].offset = RT_BSWAP_U32(paFatArches[i].offset);
+ paFatArches[i].size = RT_BSWAP_U32(paFatArches[i].size);
+ paFatArches[i].align = RT_BSWAP_U32(paFatArches[i].align);
+ }
+ }
+
+ /* Lookup our architecture in the FAT. */
+ if (pFat->magic == IMAGE_FAT_SIGNATURE)
+ {
+ if (pFat->nfat_arch > 16)
+ RETURN_VERR_BAD_EXE_FORMAT;
+
+ for (i = 0; i < pFat->nfat_arch; i++)
+ {
+ if ( paFatArches[i].cputype == MY_CPU_TYPE
+ && paFatArches[i].cpusubtype == MY_CPU_SUBTYPE_ALL)
+ {
+ pThis->offArch = paFatArches[i].offset;
+ pThis->cbArch = paFatArches[i].size;
+ if (!pThis->cbArch)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (pThis->offArch < sizeof(fat_header_t) + sizeof(fat_arch_t) * pFat->nfat_arch)
+ RETURN_VERR_BAD_EXE_FORMAT;
+ if (pThis->offArch + pThis->cbArch <= pThis->offArch)
+ RETURN_VERR_LDR_ARCH_MISMATCH;
+ break;
+ }
+ }
+ if (i >= pFat->nfat_arch)
+ RETURN_VERR_LDR_ARCH_MISMATCH;
+ }
+
+ /*
+ * Read the Mach-O header and validate it.
+ */
+ rc = RTFileReadAt(pThis->hFile, pThis->offArch, pThis->abBuf, sizeof(MY_MACHO_HEADER), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ MY_MACHO_HEADER const *pHdr = (MY_MACHO_HEADER const *)pThis->abBuf;
+ if (pHdr->magic != MY_MACHO_MAGIC)
+ {
+ if ( pHdr->magic == IMAGE_MACHO32_SIGNATURE
+ || pHdr->magic == IMAGE_MACHO32_SIGNATURE_OE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE
+ || pHdr->magic == IMAGE_MACHO64_SIGNATURE_OE)
+ RETURN_VERR_LDR_ARCH_MISMATCH;
+ RETURN_VERR_BAD_EXE_FORMAT;
+ }
+
+ if (pHdr->cputype != MY_CPU_TYPE)
+ RETURN_VERR_LDR_ARCH_MISMATCH;
+ if (pHdr->cpusubtype != MY_CPU_SUBTYPE_ALL)
+ RETURN_VERR_LDR_ARCH_MISMATCH;
+ if (pHdr->filetype != MH_EXECUTE)
+ RETURN_VERR_LDR_UNEXPECTED;
+ if (pHdr->ncmds < 4)
+ RETURN_VERR_LDR_UNEXPECTED;
+ if (pHdr->ncmds > 256)
+ RETURN_VERR_LDR_UNEXPECTED;
+ if (pHdr->sizeofcmds <= pHdr->ncmds * sizeof(load_command_t))
+ RETURN_VERR_LDR_UNEXPECTED;
+ if (pHdr->sizeofcmds >= _1M)
+ RETURN_VERR_LDR_UNEXPECTED;
+ if (pHdr->flags & ~MH_VALID_FLAGS)
+ RETURN_VERR_LDR_UNEXPECTED;
+
+ pThis->cLoadCmds = pHdr->ncmds;
+ pThis->cbLoadCmds = pHdr->sizeofcmds;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destructor.
+ *
+ * @param pThis The instance to destroy.
+ */
+static void rtR0DbgKrnlDarwinDtor(RTDBGKRNLINFOINT *pThis)
+{
+ pThis->u32Magic = ~RTDBGKRNLINFO_MAGIC;
+
+ if (!pThis->fIsInMem)
+ RTMemFree(pThis->pachStrTab);
+ pThis->pachStrTab = NULL;
+
+ if (!pThis->fIsInMem)
+ RTMemFree(pThis->paSyms);
+ pThis->paSyms = NULL;
+
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Completes a handle, logging details.
+ *
+ * @returns VINF_SUCCESS
+ * @param phKrnlInfo Where to return the handle.
+ * @param pThis The instance to complete.
+ * @param pszKernelFile What kernel file it's based on.
+ */
+static int rtR0DbgKrnlDarwinSuccess(PRTDBGKRNLINFO phKrnlInfo, RTDBGKRNLINFOINT *pThis, const char *pszKernelFile)
+{
+ pThis->u32Magic = RTDBGKRNLINFO_MAGIC;
+ pThis->cRefs = 1;
+
+#if defined(DEBUG) || defined(IN_RING3)
+ LOG_SUCCESS("RTR0DbgKrnlInfoOpen: Found: %#zx + %#zx - %s\n", pThis->uTextSegLinkAddr, pThis->offLoad, pszKernelFile);
+#else
+ LOG_SUCCESS("RTR0DbgKrnlInfoOpen: Found: %s\n", pszKernelFile);
+#endif
+ LOG_SUCCESS("RTR0DbgKrnlInfoOpen: SDK version: %u.%u.%u MinOS version: %u.%u.%u Source version: %u.%u.%u.%u.%u\n",
+ pThis->uSdkVer >> 16, (pThis->uSdkVer >> 8) & 0xff, pThis->uSdkVer & 0xff,
+ pThis->uMinOsVer >> 16, (pThis->uMinOsVer >> 8) & 0xff, pThis->uMinOsVer & 0xff,
+ (uint32_t)(pThis->uSrcVer >> 40),
+ (uint32_t)(pThis->uSrcVer >> 30) & 0x3ff,
+ (uint32_t)(pThis->uSrcVer >> 20) & 0x3ff,
+ (uint32_t)(pThis->uSrcVer >> 10) & 0x3ff,
+ (uint32_t)(pThis->uSrcVer) & 0x3ff);
+
+ *phKrnlInfo = pThis;
+ return VINF_SUCCESS;
+}
+
+
+static int rtR0DbgKrnlDarwinOpen(PRTDBGKRNLINFO phKrnlInfo, const char *pszKernelFile)
+{
+ RTDBGKRNLINFOINT *pThis = (RTDBGKRNLINFOINT *)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ pThis->hFile = NIL_RTFILE;
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aoffLoadSegments); i++)
+ pThis->aoffLoadSegments[i] = UINTPTR_MAX;
+
+ int rc = RTFileOpen(&pThis->hFile, pszKernelFile, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ rc = rtR0DbgKrnlDarwinLoadFileHeaders(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtR0DbgKrnlDarwinLoadCommands(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtR0DbgKrnlDarwinLoadSymTab(pThis, pszKernelFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtR0DbgKrnlDarwinInitLoadDisplacements(pThis);
+ if (RT_SUCCESS(rc))
+ rc = rtR0DbgKrnlDarwinCheckStandardSymbols(pThis, pszKernelFile);
+ }
+
+ rtR0DbgKrnlDarwinLoadDone(pThis);
+ if (RT_SUCCESS(rc))
+ rtR0DbgKrnlDarwinSuccess(phKrnlInfo, pThis, pszKernelFile);
+ else
+ rtR0DbgKrnlDarwinDtor(pThis);
+ return rc;
+}
+
+
+#ifdef IN_RING0
+
+/**
+ * Checks if a page is present.
+ * @returns true if it is, false if it isn't.
+ * @param uPageAddr The address of/in the page to check.
+ */
+static bool rtR0DbgKrnlDarwinIsPagePresent(uintptr_t uPageAddr)
+{
+ /** @todo the dtrace code subjects the result to pmap_is_valid, but that
+ * isn't exported, so we'll have to make to with != 0 here. */
+ return pmap_find_phys(kernel_pmap, uPageAddr) != 0;
+}
+
+
+/**
+ * Used to check whether a memory range is present or not.
+ *
+ * This is applied to the to the load commands and selected portions of the link
+ * edit segment.
+ *
+ * @returns true if all present, false if not.
+ * @param uAddress The start address.
+ * @param cb Number of bytes to check.
+ * @param pszWhat What we're checking, for logging.
+ * @param pHdr The header address (for logging).
+ */
+static bool rtR0DbgKrnlDarwinIsRangePresent(uintptr_t uAddress, size_t cb,
+ const char *pszWhat, MY_MACHO_HEADER const volatile *pHdr)
+{
+ uintptr_t const uStartAddress = uAddress;
+ intptr_t cPages = RT_ALIGN_Z(cb + (uAddress & PAGE_OFFSET_MASK), PAGE_SIZE);
+ RT_NOREF(uStartAddress, pszWhat, pHdr);
+ for (;;)
+ {
+ if (!rtR0DbgKrnlDarwinIsPagePresent(uAddress))
+ {
+ LOG_NOT_PRESENT("RTR0DbgInfo: %p: Page in %s is not present: %#zx - rva %#zx; in structure %#zx (%#zx LB %#zx)\n",
+ (void *)pHdr, pszWhat, uAddress, uAddress - (uintptr_t)pHdr, uAddress - uStartAddress, uStartAddress, cb);
+ return false;
+ }
+
+ cPages -= 1;
+ if (cPages <= 0)
+ uAddress += PAGE_SIZE;
+ else
+ return true;
+ }
+}
+
+
+/**
+ * Try "open" the in-memory kernel image
+ *
+ * @returns IPRT stauts code
+ * @param phKrnlInfo Where to return the info instance on success.
+ */
+static int rtR0DbgKrnlDarwinOpenInMemory(PRTDBGKRNLINFO phKrnlInfo)
+{
+ RTDBGKRNLINFOINT *pThis = (RTDBGKRNLINFOINT *)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ pThis->hFile = NIL_RTFILE;
+ pThis->fIsInMem = true;
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aoffLoadSegments); i++)
+ pThis->aoffLoadSegments[i] = UINTPTR_MAX;
+
+ /*
+ * Figure the search range based on a symbol that is supposed to be in
+ * kernel text segment, using it as the upper boundrary. The lower boundary
+ * is determined by subtracting a max kernel size of 64MB (the largest kernel
+ * file, kernel.kasan, is around 45MB, but the end of __TEXT is about 27 MB,
+ * which means we should still have plenty of room for future growth with 64MB).
+ */
+ uintptr_t const uSomeKernelAddr = (uintptr_t)&absolutetime_to_nanoseconds;
+ uintptr_t const uLowestKernelAddr = uSomeKernelAddr - _64M;
+
+ /*
+ * The kernel is probably aligned at some boundrary larger than a page size,
+ * so to speed things up we start by assuming the alignment is page directory
+ * sized. In case we're wrong and it's smaller, we decrease the alignment till
+ * we've reach the page size.
+ */
+ uintptr_t fPrevAlignMask = ~(uintptr_t)0;
+ uintptr_t uCurAlign = _2M; /* ASSUMES the kernel is typically 2MB aligned. */
+ while (uCurAlign >= PAGE_SIZE)
+ {
+ /*
+ * Search down from the symbol address looking for a mach-O header that
+ * looks like it might belong to the kernel.
+ */
+ for (uintptr_t uCur = uSomeKernelAddr & ~(uCurAlign - 1); uCur >= uLowestKernelAddr; uCur -= uCurAlign)
+ {
+ /* Skip pages we've checked in previous iterations and pages that aren't present: */
+ /** @todo This is a little bogus in case the header is paged out. */
+ if ( (uCur & fPrevAlignMask)
+ && rtR0DbgKrnlDarwinIsPagePresent(uCur))
+ {
+ /*
+ * Look for valid mach-o header (we skip cpusubtype on purpose here).
+ */
+ MY_MACHO_HEADER const volatile *pHdr = (MY_MACHO_HEADER const volatile *)uCur;
+ if ( pHdr->magic == MY_MACHO_MAGIC
+ && pHdr->filetype == MH_EXECUTE
+ && pHdr->cputype == MY_CPU_TYPE)
+ {
+ /* More header validation: */
+ pThis->cLoadCmds = pHdr->ncmds;
+ pThis->cbLoadCmds = pHdr->sizeofcmds;
+ if (pHdr->ncmds < 4)
+ LOG_MISMATCH("RTR0DbgInfo: %p: ncmds=%u is too small\n", (void *)pHdr, pThis->cLoadCmds);
+ else if (pThis->cLoadCmds > 256)
+ LOG_MISMATCH("RTR0DbgInfo: %p: ncmds=%u is too big\n", (void *)pHdr, pThis->cLoadCmds);
+ else if (pThis->cbLoadCmds <= pThis->cLoadCmds * sizeof(load_command_t))
+ LOG_MISMATCH("RTR0DbgInfo: %p: sizeofcmds=%u is too small for ncmds=%u\n",
+ (void *)pHdr, pThis->cbLoadCmds, pThis->cLoadCmds);
+ else if (pThis->cbLoadCmds >= _1M)
+ LOG_MISMATCH("RTR0DbgInfo: %p: sizeofcmds=%u is too big\n", (void *)pHdr, pThis->cbLoadCmds);
+ else if (pHdr->flags & ~MH_VALID_FLAGS)
+ LOG_MISMATCH("RTR0DbgInfo: %p: invalid flags=%#x\n", (void *)pHdr, pHdr->flags);
+ /*
+ * Check that we can safely read the load commands, then parse & validate them.
+ */
+ else if (rtR0DbgKrnlDarwinIsRangePresent((uintptr_t)(pHdr + 1), pThis->cbLoadCmds, "load commands", pHdr))
+ {
+ pThis->pLoadCmds = (load_command_t *)(pHdr + 1);
+ int rc = rtR0DbgKrnlDarwinParseCommands(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /* Calculate the slide value. This is typically zero as the
+ load commands has been relocated (the case with 10.14.0 at least). */
+ /** @todo ASSUMES that the __TEXT segment comes first and includes the
+ * mach-o header and load commands and all that. */
+ pThis->offLoad = uCur - pThis->uTextSegLinkAddr;
+
+ /* Check that the kernel symbol is in the text segment: */
+ uintptr_t const offSomeKernAddr = uSomeKernelAddr - uCur;
+ if (offSomeKernAddr >= pThis->cbTextSeg)
+ LOG_MISMATCH("RTR0DbgInfo: %p: Our symbol at %zx (off %zx) isn't within the text segment (size %#zx)\n",
+ (void *)pHdr, uSomeKernelAddr, offSomeKernAddr, pThis->cbTextSeg);
+ /*
+ * Parse the symbol+string tables.
+ */
+ else if (pThis->uSymTabLinkAddr == 0)
+ LOG_MISMATCH("RTR0DbgInfo: %p: No symbol table VA (off %#x L %#x)\n",
+ (void *)pHdr, pThis->offSyms, pThis->cSyms);
+ else if (pThis->uStrTabLinkAddr == 0)
+ LOG_MISMATCH("RTR0DbgInfo: %p: No string table VA (off %#x LB %#x)\n",
+ (void *)pHdr, pThis->offSyms, pThis->cbStrTab);
+ else if ( rtR0DbgKrnlDarwinIsRangePresent(pThis->uStrTabLinkAddr + pThis->offLoad,
+ pThis->cbStrTab, "string table", pHdr)
+ && rtR0DbgKrnlDarwinIsRangePresent(pThis->uSymTabLinkAddr + pThis->offLoad,
+ pThis->cSyms * sizeof(pThis->paSyms),
+ "symbol table", pHdr))
+ {
+ pThis->pachStrTab = (char *)pThis->uStrTabLinkAddr + pThis->offLoad;
+ pThis->paSyms = (MY_NLIST *)pThis->uSymTabLinkAddr + pThis->offLoad;
+ rc = rtR0DbgKrnlDarwinParseSymTab(pThis, "in-memory");
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtR0DbgKrnlDarwinInitLoadDisplacements(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finally check the standard candles.
+ */
+ rc = rtR0DbgKrnlDarwinCheckStandardSymbols(pThis, "in-memory");
+ rtR0DbgKrnlDarwinLoadDone(pThis);
+ if (RT_SUCCESS(rc))
+ return rtR0DbgKrnlDarwinSuccess(phKrnlInfo, pThis, "in-memory");
+ }
+ }
+ }
+ }
+
+ RT_ZERO(pThis->apSections);
+ RT_ZERO(pThis->apSegments);
+ pThis->pLoadCmds = NULL;
+ }
+ }
+ }
+ }
+
+ fPrevAlignMask = uCurAlign - 1;
+ uCurAlign >>= 1;
+ }
+
+ RTMemFree(pThis);
+ return VERR_GENERAL_FAILURE;
+}
+
+#endif /* IN_RING0 */
+
+RTR0DECL(int) RTR0DbgKrnlInfoOpen(PRTDBGKRNLINFO phKrnlInfo, uint32_t fFlags)
+{
+ AssertPtrReturn(phKrnlInfo, VERR_INVALID_POINTER);
+ *phKrnlInfo = NIL_RTDBGKRNLINFO;
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+
+#ifdef IN_RING0
+ /*
+ * Try see if we can use the kernel memory directly. This depends on not
+ * having the __LINKEDIT segment jettisoned or swapped out. For older
+ * kernels this is typically the case, unless kallsyms=1 is in boot-args.
+ */
+ int rc = rtR0DbgKrnlDarwinOpenInMemory(phKrnlInfo);
+ if (RT_SUCCESS(rc))
+ {
+ Log(("RTR0DbgKrnlInfoOpen: Using in-memory kernel.\n"));
+ return rc;
+ }
+#else
+ int rc = VERR_WRONG_ORDER; /* shut up stupid MSC */
+#endif
+
+ /*
+ * Go thru likely kernel locations
+ *
+ * Note! Check the OS X version and reorder the list?
+ * Note! We should try fish kcsuffix out of bootargs or somewhere one day.
+ */
+ static bool s_fFirstCall = true;
+#ifdef IN_RING3
+ extern const char *g_pszTestKernel;
+#endif
+ struct
+ {
+ const char *pszLocation;
+ int rc;
+ } aKernels[] =
+ {
+#ifdef IN_RING3
+ { g_pszTestKernel, VERR_WRONG_ORDER },
+#endif
+ { "/System/Library/Kernels/kernel", VERR_WRONG_ORDER },
+ { "/System/Library/Kernels/kernel.development", VERR_WRONG_ORDER },
+ { "/System/Library/Kernels/kernel.debug", VERR_WRONG_ORDER },
+ { "/mach_kernel", VERR_WRONG_ORDER },
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(aKernels); i++)
+ {
+ aKernels[i].rc = rc = rtR0DbgKrnlDarwinOpen(phKrnlInfo, aKernels[i].pszLocation);
+ if (RT_SUCCESS(rc))
+ {
+ if (s_fFirstCall)
+ {
+ printf("RTR0DbgKrnlInfoOpen: Using kernel file '%s'\n", aKernels[i].pszLocation);
+ s_fFirstCall = false;
+ }
+ return rc;
+ }
+ }
+
+ /*
+ * Failed.
+ */
+ /* Pick the best error code. */
+ for (uint32_t i = 0; rc == VERR_FILE_NOT_FOUND && i < RT_ELEMENTS(aKernels); i++)
+ if (aKernels[i].rc != VERR_FILE_NOT_FOUND)
+ rc = aKernels[i].rc;
+
+ /* Bitch about it. */
+ printf("RTR0DbgKrnlInfoOpen: failed to find matching kernel file! rc=%d\n", rc);
+ if (s_fFirstCall)
+ {
+ for (uint32_t i = 0; i < RT_ELEMENTS(aKernels); i++)
+ printf("RTR0DbgKrnlInfoOpen: '%s' -> %d\n", aKernels[i].pszLocation, aKernels[i].rc);
+ s_fFirstCall = false;
+ }
+
+ return rc;
+}
+
+
+RTR0DECL(uint32_t) RTR0DbgKrnlInfoRetain(RTDBGKRNLINFO hKrnlInfo)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+ return cRefs;
+}
+
+
+RTR0DECL(uint32_t) RTR0DbgKrnlInfoRelease(RTDBGKRNLINFO hKrnlInfo)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ if (pThis == NIL_RTDBGKRNLINFO)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (cRefs == 0)
+ rtR0DbgKrnlDarwinDtor(pThis);
+ return cRefs;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQueryMember(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszStructure,
+ const char *pszMember, size_t *poffMember)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszMember, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszModule, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszStructure, VERR_INVALID_POINTER);
+ AssertPtrReturn(poffMember, VERR_INVALID_POINTER);
+ return VERR_NOT_FOUND;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQuerySymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule,
+ const char *pszSymbol, void **ppvSymbol)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszSymbol, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(ppvSymbol, VERR_INVALID_PARAMETER);
+ AssertReturn(!pszModule, VERR_MODULE_NOT_FOUND);
+
+ uintptr_t uValue = rtR0DbgKrnlDarwinLookup(pThis, pszSymbol);
+ if (ppvSymbol)
+ *ppvSymbol = (void *)uValue;
+ if (uValue)
+ return VINF_SUCCESS;
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/fileio-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/fileio-r0drv-darwin.cpp
new file mode 100644
index 00000000..e810828a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/fileio-r0drv-darwin.cpp
@@ -0,0 +1,323 @@
+/* $Id: fileio-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - File I/O, R0 Driver, Darwin.
+ */
+
+/*
+ * 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-darwin-kernel.h"
+
+#include <iprt/file.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Default file permissions for newly created files. */
+#if defined(S_IRUSR) && defined(S_IWUSR)
+# define RT_FILE_PERMISSION (S_IRUSR | S_IWUSR)
+#else
+# define RT_FILE_PERMISSION (00600)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Darwin kernel file handle data.
+ */
+typedef struct RTFILEINT
+{
+ /** Magic value (RTFILE_MAGIC). */
+ uint32_t u32Magic;
+ /** The open mode flags passed to the kernel API. */
+ int fOpenMode;
+ /** The open flags passed to RTFileOpen. */
+ uint64_t fOpen;
+ /** The VFS context in which the file was opened. */
+ vfs_context_t hVfsCtx;
+ /** The vnode returned by vnode_open. */
+ vnode_t hVnode;
+ /** The current file offset. */
+ uint64_t offFile;
+} RTFILEINT;
+/** Magic number for RTFILEINT::u32Magic (To Be Determined). */
+#define RTFILE_MAGIC UINT32_C(0x01020304)
+
+
+RTDECL(int) RTFileOpen(PRTFILE phFile, const char *pszFilename, uint64_t fOpen)
+{
+ AssertReturn(!(fOpen & RTFILE_O_TEMP_AUTO_DELETE), VERR_NOT_SUPPORTED);
+
+ RTFILEINT *pThis = (RTFILEINT *)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ errno_t rc;
+ pThis->u32Magic = RTFILE_MAGIC;
+ pThis->fOpen = fOpen;
+ pThis->hVfsCtx = vfs_context_current();
+ if (pThis->hVfsCtx != NULL)
+ {
+ int fCMode = (fOpen & RTFILE_O_CREATE_MODE_MASK)
+ ? (fOpen & RTFILE_O_CREATE_MODE_MASK) >> RTFILE_O_CREATE_MODE_SHIFT
+ : RT_FILE_PERMISSION;
+ int fVnFlags = 0; /* VNODE_LOOKUP_XXX */
+ int fOpenMode = 0;
+ if (fOpen & RTFILE_O_NON_BLOCK)
+ fOpenMode |= O_NONBLOCK;
+ if (fOpen & RTFILE_O_WRITE_THROUGH)
+ fOpenMode |= O_SYNC;
+
+ /* create/truncate file */
+ switch (fOpen & RTFILE_O_ACTION_MASK)
+ {
+ case RTFILE_O_OPEN: break;
+ case RTFILE_O_OPEN_CREATE: fOpenMode |= O_CREAT; break;
+ case RTFILE_O_CREATE: fOpenMode |= O_CREAT | O_EXCL; break;
+ case RTFILE_O_CREATE_REPLACE: fOpenMode |= O_CREAT | O_TRUNC; break; /** @todo replacing needs fixing, this is *not* a 1:1 mapping! */
+ }
+ if (fOpen & RTFILE_O_TRUNCATE)
+ fOpenMode |= O_TRUNC;
+
+ switch (fOpen & RTFILE_O_ACCESS_MASK)
+ {
+ case RTFILE_O_READ:
+ fOpenMode |= FREAD;
+ break;
+ case RTFILE_O_WRITE:
+ fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | FWRITE : FWRITE;
+ break;
+ case RTFILE_O_READWRITE:
+ fOpenMode |= fOpen & RTFILE_O_APPEND ? O_APPEND | FWRITE | FREAD : FWRITE | FREAD;
+ break;
+ default:
+ AssertMsgFailed(("RTFileOpen received an invalid RW value, fOpen=%#x\n", fOpen));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_INVALID_PARAMETER;
+ }
+
+ pThis->fOpenMode = fOpenMode;
+ rc = vnode_open(pszFilename, fOpenMode, fCMode, fVnFlags, &pThis->hVnode, pThis->hVfsCtx);
+ if (rc == 0)
+ {
+ *phFile = pThis;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromErrno(rc);
+ }
+ else
+ rc = VERR_INTERNAL_ERROR_5;
+ RTMemFree(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+RTDECL(int) RTFileClose(RTFILE hFile)
+{
+ if (hFile == NIL_RTFILE)
+ return VINF_SUCCESS;
+
+ RTFILEINT *pThis = hFile;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTFILE_MAGIC, VERR_INVALID_HANDLE);
+ pThis->u32Magic = ~RTFILE_MAGIC;
+
+ IPRT_DARWIN_SAVE_EFL_AC();
+ errno_t rc = vnode_close(pThis->hVnode, pThis->fOpenMode & (FREAD | FWRITE), pThis->hVfsCtx);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+
+ RTMemFree(pThis);
+ return RTErrConvertFromErrno(rc);
+}
+
+
+RTDECL(int) RTFileReadAt(RTFILE hFile, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTFILEINT *pThis = hFile;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTFILE_MAGIC, VERR_INVALID_HANDLE);
+
+ off_t offNative = (off_t)off;
+ AssertReturn((RTFOFF)offNative == off, VERR_OUT_OF_RANGE);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+#if 0 /* Added in 10.6, grr. */
+ errno_t rc;
+ if (!pcbRead)
+ rc = vn_rdwr(UIO_READ, pThis->hVnode, (char *)pvBuf, cbToRead, offNative, UIO_SYSSPACE, 0 /*ioflg*/,
+ vfs_context_ucred(pThis->hVfsCtx), NULL, vfs_context_proc(pThis->hVfsCtx));
+ else
+ {
+ int cbLeft = 0;
+ rc = vn_rdwr(UIO_READ, pThis->hVnode, (char *)pvBuf, cbToRead, offNative, UIO_SYSSPACE, 0 /*ioflg*/,
+ vfs_context_ucred(pThis->hVfsCtx), &cbLeft, vfs_context_proc(pThis->hVfsCtx));
+ *pcbRead = cbToRead - cbLeft;
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return !rc ? VINF_SUCCESS : RTErrConvertFromErrno(rc);
+
+#else
+ uio_t hUio = uio_create(1, offNative, UIO_SYSSPACE, UIO_READ);
+ if (!hUio)
+ {
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+ errno_t rc;
+ if (uio_addiov(hUio, (user_addr_t)(uintptr_t)pvBuf, cbToRead) == 0)
+ {
+ rc = VNOP_READ(pThis->hVnode, hUio, 0 /*ioflg*/, pThis->hVfsCtx);
+ off_t const cbActual = cbToRead - uio_resid(hUio);
+ if (pcbRead)
+ *pcbRead = cbActual;
+ if (rc == 0)
+ {
+ pThis->offFile += (uint64_t)cbActual;
+ if (cbToRead != (uint64_t)cbActual)
+ rc = VERR_FILE_IO_ERROR;
+ }
+ else
+ rc = RTErrConvertFromErrno(rc);
+ }
+ else
+ rc = VERR_INTERNAL_ERROR_3;
+ uio_free(hUio);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+#endif
+}
+
+
+RTDECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTFILEINT *pThis = hFile;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTFILE_MAGIC, VERR_INVALID_HANDLE);
+
+ return RTFileReadAt(hFile, pThis->offFile, pvBuf, cbToRead, pcbRead);
+}
+
+
+RTDECL(int) RTFileQuerySize(RTFILE hFile, uint64_t *pcbSize)
+{
+ RTFILEINT *pThis = hFile;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTFILE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Query the data size attribute.
+ * Note! Allocate extra attribute buffer space to be on the safe side.
+ */
+ union
+ {
+ struct vnode_attr VAttr;
+ uint8_t abPadding[sizeof(struct vnode_attr) * 2];
+ } uBuf;
+ RT_ZERO(uBuf);
+ struct vnode_attr *pVAttr = &uBuf.VAttr;
+
+ VATTR_INIT(pVAttr);
+ VATTR_WANTED(pVAttr, va_data_size);
+
+ errno_t rc = vnode_getattr(pThis->hVnode, pVAttr, pThis->hVfsCtx);
+ if (!rc)
+ {
+ *pcbSize = pVAttr->va_data_size;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(rc);
+}
+
+
+RTDECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
+{
+ RTFILEINT *pThis = hFile;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTFILE_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t offNew;
+ switch (uMethod)
+ {
+ case RTFILE_SEEK_BEGIN:
+ AssertReturn(offSeek >= 0, VERR_NEGATIVE_SEEK);
+ offNew = offSeek;
+ break;
+
+ case RTFILE_SEEK_CURRENT:
+ offNew = pThis->offFile + offSeek;
+ break;
+
+ case RTFILE_SEEK_END:
+ {
+ uint64_t cbFile = 0;
+ int rc = RTFileQuerySize(hFile, &cbFile);
+ if (RT_SUCCESS(rc))
+ offNew = cbFile + offSeek;
+ else
+ return rc;
+ break;
+ }
+
+ default:
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if ((RTFOFF)offNew >= 0)
+ {
+ pThis->offFile = offNew;
+ if (poffActual)
+ *poffActual = offNew;
+ return VINF_SUCCESS;
+ }
+ return VERR_NEGATIVE_SEEK;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/initterm-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/initterm-r0drv-darwin.cpp
new file mode 100644
index 00000000..6535f966
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/initterm-r0drv-darwin.cpp
@@ -0,0 +1,133 @@
+/* $Id: initterm-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Initialization & Termination, R0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/dbg.h>
+#include "internal/initterm.h"
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to the lock group used by IPRT. */
+DECL_HIDDEN_DATA(lck_grp_t *) g_pDarwinLockGroup = NULL;
+/** Pointer to the ast_pending function, if found. */
+DECL_HIDDEN_DATA(PFNR0DARWINASTPENDING) g_pfnR0DarwinAstPending = NULL;
+/** Pointer to the cpu_interrupt function, if found. */
+DECL_HIDDEN_DATA(PFNR0DARWINCPUINTERRUPT) g_pfnR0DarwinCpuInterrupt = NULL;
+#ifdef DEBUG
+/** Pointer to the vm_fault_external function - used once for debugging @bugref{9466}. */
+DECL_HIDDEN_DATA(PFNR0DARWINVMFAULTEXTERNAL) g_pfnR0DarwinVmFaultExternal = NULL;
+#endif
+
+
+DECLHIDDEN(int) rtR0InitNative(void)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Create the lock group.
+ */
+ g_pDarwinLockGroup = lck_grp_alloc_init("IPRT", LCK_GRP_ATTR_NULL);
+ AssertReturn(g_pDarwinLockGroup, VERR_NO_MEMORY);
+
+ /*
+ * Initialize the preemption hacks.
+ */
+ int rc = rtThreadPreemptDarwinInit();
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try resolve kernel symbols we need but apple don't wish to give us.
+ */
+ RTDBGKRNLINFO hKrnlInfo;
+ rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "ast_pending", (void **)&g_pfnR0DarwinAstPending);
+ printf("ast_pending=%p\n", (void *)(uintptr_t)g_pfnR0DarwinAstPending);
+ RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "cpu_interrupt", (void **)&g_pfnR0DarwinCpuInterrupt);
+ printf("cpu_interrupt=%p\n", (void *)(uintptr_t)g_pfnR0DarwinCpuInterrupt);
+#ifdef DEBUG
+ RTR0DbgKrnlInfoQuerySymbol(hKrnlInfo, NULL, "vm_fault_external", (void **)&g_pfnR0DarwinVmFaultExternal);
+ printf("vm_fault_external=%p\n", (void *)(uintptr_t)g_pfnR0DarwinVmFaultExternal);
+#endif
+ RTR0DbgKrnlInfoRelease(hKrnlInfo);
+ }
+ if (RT_FAILURE(rc))
+ {
+ printf("rtR0InitNative: warning! failed to resolve special kernel symbols\n");
+ rc = VINF_SUCCESS;
+ }
+ }
+ if (RT_FAILURE(rc))
+ rtR0TermNative();
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtR0TermNative(void)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Preemption hacks before the lock group.
+ */
+ rtThreadPreemptDarwinTerm();
+
+ /*
+ * Free the lock group.
+ */
+ if (g_pDarwinLockGroup)
+ {
+ lck_grp_free(g_pDarwinLockGroup);
+ g_pDarwinLockGroup = NULL;
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/memobj-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/memobj-r0drv-darwin.cpp
new file mode 100644
index 00000000..66f8327f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/memobj-r0drv-darwin.cpp
@@ -0,0 +1,1571 @@
+/* $Id: memobj-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, Darwin.
+ */
+
+/*
+ * 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 RTMEM_NO_WRAP_TO_EF_APIS /* circular dependency otherwise. */
+#include "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/memobj.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/x86.h>
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "internal/memobj.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define MY_PRINTF(...) do { printf(__VA_ARGS__); kprintf(__VA_ARGS__); } while (0)
+
+/*#define USE_VM_MAP_WIRE - may re-enable later when non-mapped allocations are added. */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The Darwin version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJDARWIN
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ /** Pointer to the memory descriptor created for allocated and locked memory. */
+ IOMemoryDescriptor *pMemDesc;
+ /** Pointer to the memory mapping object for mapped memory. */
+ IOMemoryMap *pMemMap;
+} RTR0MEMOBJDARWIN, *PRTR0MEMOBJDARWIN;
+
+/**
+ * Common thread_call_allocate/thread_call_enter argument package.
+ */
+typedef struct RTR0MEMOBJDARWINTHREADARGS
+{
+ int32_t volatile rc;
+ RTSEMEVENTMULTI hEvent;
+} RTR0MEMOBJDARWINTHREADARGS;
+
+
+/**
+ * Arguments for rtR0MemObjNativeAllockWorkOnKernelThread.
+ */
+typedef struct RTR0MEMOBJDARWINALLOCARGS
+{
+ RTR0MEMOBJDARWINTHREADARGS Core;
+ PPRTR0MEMOBJINTERNAL ppMem;
+ size_t cb;
+ bool fExecutable;
+ bool fContiguous;
+ mach_vm_address_t PhysMask;
+ uint64_t MaxPhysAddr;
+ RTR0MEMOBJTYPE enmType;
+ size_t uAlignment;
+ const char *pszTag;
+} RTR0MEMOBJDARWINALLOCARGS;
+
+/**
+ * Arguments for rtR0MemObjNativeProtectWorkOnKernelThread.
+ */
+typedef struct RTR0MEMOBJDARWINPROTECTARGS
+{
+ RTR0MEMOBJDARWINTHREADARGS Core;
+ PRTR0MEMOBJINTERNAL pMem;
+ size_t offSub;
+ size_t cbSub;
+ uint32_t fProt;
+} RTR0MEMOBJDARWINPROTECTARGS;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtR0MemObjNativeAllockWorkerOnKernelThread(void *pvUser0, void *pvUser1);
+static int rtR0MemObjNativeProtectWorker(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt);
+static void rtR0MemObjNativeProtectWorkerOnKernelThread(void *pvUser0, void *pvUser1);
+
+
+/**
+ * Touch the pages to force the kernel to create or write-enable the page table
+ * entries.
+ *
+ * This is necessary since the kernel gets upset if we take a page fault when
+ * preemption is disabled and/or we own a simple lock (same thing). It has no
+ * problems with us disabling interrupts when taking the traps, weird stuff.
+ *
+ * (This is basically a way of invoking vm_fault on a range of pages.)
+ *
+ * @param pv Pointer to the first page.
+ * @param cb The number of bytes.
+ */
+static void rtR0MemObjDarwinTouchPages(void *pv, size_t cb)
+{
+ uint32_t volatile *pu32 = (uint32_t volatile *)pv;
+ for (;;)
+ {
+ ASMAtomicCmpXchgU32(pu32, 0xdeadbeef, 0xdeadbeef);
+ if (cb <= PAGE_SIZE)
+ break;
+ cb -= PAGE_SIZE;
+ pu32 += PAGE_SIZE / sizeof(uint32_t);
+ }
+}
+
+
+/**
+ * Read (sniff) every page in the range to make sure there are some page tables
+ * entries backing it.
+ *
+ * This is just to be sure vm_protect didn't remove stuff without re-adding it
+ * if someone should try write-protect something.
+ *
+ * @param pv Pointer to the first page.
+ * @param cb The number of bytes.
+ */
+static void rtR0MemObjDarwinSniffPages(void const *pv, size_t cb)
+{
+ uint32_t volatile *pu32 = (uint32_t volatile *)pv;
+ uint32_t volatile u32Counter = 0;
+ for (;;)
+ {
+ u32Counter += *pu32;
+
+ if (cb <= PAGE_SIZE)
+ break;
+ cb -= PAGE_SIZE;
+ pu32 += PAGE_SIZE / sizeof(uint32_t);
+ }
+}
+
+
+/**
+ * Gets the virtual memory map the specified object is mapped into.
+ *
+ * @returns VM map handle on success, NULL if no map.
+ * @param pMem The memory object.
+ */
+DECLINLINE(vm_map_t) rtR0MemObjDarwinGetMap(PRTR0MEMOBJINTERNAL pMem)
+{
+ switch (pMem->enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ return kernel_map;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ if (pMem->pv)
+ return kernel_map;
+ return NULL;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ return pMem->u.Lock.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : get_task_map((task_t)pMem->u.Lock.R0Process);
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ return pMem->u.ResVirt.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : get_task_map((task_t)pMem->u.ResVirt.R0Process);
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ return pMem->u.Mapping.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : get_task_map((task_t)pMem->u.Mapping.R0Process);
+
+ default:
+ return NULL;
+ }
+}
+
+#if 0 /* not necessary after all*/
+/* My vm_map mockup. */
+struct my_vm_map
+{
+ struct { char pad[8]; } lock;
+ struct my_vm_map_header
+ {
+ struct vm_map_links
+ {
+ void *prev;
+ void *next;
+ vm_map_offset_t start;
+ vm_map_offset_t end;
+ } links;
+ int nentries;
+ boolean_t entries_pageable;
+ } hdr;
+ pmap_t pmap;
+ vm_map_size_t size;
+};
+
+
+/**
+ * Gets the minimum map address, this is similar to get_map_min.
+ *
+ * @returns The start address of the map.
+ * @param pMap The map.
+ */
+static vm_map_offset_t rtR0MemObjDarwinGetMapMin(vm_map_t pMap)
+{
+ /* lazy discovery of the correct offset. The apple guys is a wonderfully secretive bunch. */
+ static int32_t volatile s_offAdjust = INT32_MAX;
+ int32_t off = s_offAdjust;
+ if (off == INT32_MAX)
+ {
+ for (off = 0; ; off += sizeof(pmap_t))
+ {
+ if (*(pmap_t *)((uint8_t *)kernel_map + off) == kernel_pmap)
+ break;
+ AssertReturn(off <= RT_MAX(RT_OFFSETOF(struct my_vm_map, pmap) * 4, 1024), 0x1000);
+ }
+ ASMAtomicWriteS32(&s_offAdjust, off - RT_OFFSETOF(struct my_vm_map, pmap));
+ }
+
+ /* calculate it. */
+ struct my_vm_map *pMyMap = (struct my_vm_map *)((uint8_t *)pMap + off);
+ return pMyMap->hdr.links.start;
+}
+#endif /* unused */
+
+#ifdef RT_STRICT
+# if 0 /* unused */
+
+/**
+ * Read from a physical page.
+ *
+ * @param HCPhys The address to start reading at.
+ * @param cb How many bytes to read.
+ * @param pvDst Where to put the bytes. This is zero'd on failure.
+ */
+static void rtR0MemObjDarwinReadPhys(RTHCPHYS HCPhys, size_t cb, void *pvDst)
+{
+ memset(pvDst, '\0', cb);
+
+ IOAddressRange aRanges[1] = { { (mach_vm_address_t)HCPhys, RT_ALIGN_Z(cb, PAGE_SIZE) } };
+ IOMemoryDescriptor *pMemDesc = IOMemoryDescriptor::withAddressRanges(&aRanges[0], RT_ELEMENTS(aRanges),
+ kIODirectionIn, NULL /*task*/);
+ if (pMemDesc)
+ {
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ IOMemoryMap *pMemMap = pMemDesc->createMappingInTask(kernel_task, 0, kIOMapAnywhere | kIOMapDefaultCache);
+#else
+ IOMemoryMap *pMemMap = pMemDesc->map(kernel_task, 0, kIOMapAnywhere | kIOMapDefaultCache);
+#endif
+ if (pMemMap)
+ {
+ void const *pvSrc = (void const *)(uintptr_t)pMemMap->getVirtualAddress();
+ memcpy(pvDst, pvSrc, cb);
+ pMemMap->release();
+ }
+ else
+ MY_PRINTF("rtR0MemObjDarwinReadPhys: createMappingInTask failed; HCPhys=%llx\n", HCPhys);
+
+ pMemDesc->release();
+ }
+ else
+ MY_PRINTF("rtR0MemObjDarwinReadPhys: withAddressRanges failed; HCPhys=%llx\n", HCPhys);
+}
+
+
+/**
+ * Gets the PTE for a page.
+ *
+ * @returns the PTE.
+ * @param pvPage The virtual address to get the PTE for.
+ */
+static uint64_t rtR0MemObjDarwinGetPTE(void *pvPage)
+{
+ RTUINT64U u64;
+ RTCCUINTREG cr3 = ASMGetCR3();
+ RTCCUINTREG cr4 = ASMGetCR4();
+ bool fPAE = false;
+ bool fLMA = false;
+ if (cr4 & X86_CR4_PAE)
+ {
+ fPAE = true;
+ uint32_t fExtFeatures = ASMCpuId_EDX(0x80000001);
+ if (fExtFeatures & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE)
+ {
+ uint64_t efer = ASMRdMsr(MSR_K6_EFER);
+ if (efer & MSR_K6_EFER_LMA)
+ fLMA = true;
+ }
+ }
+
+ if (fLMA)
+ {
+ /* PML4 */
+ rtR0MemObjDarwinReadPhys((cr3 & ~(RTCCUINTREG)PAGE_OFFSET_MASK) | (((uint64_t)(uintptr_t)pvPage >> X86_PML4_SHIFT) & X86_PML4_MASK) * 8, 8, &u64);
+ if (!(u64.u & X86_PML4E_P))
+ {
+ MY_PRINTF("rtR0MemObjDarwinGetPTE: %p -> PML4E !p\n", pvPage);
+ return 0;
+ }
+
+ /* PDPTR */
+ rtR0MemObjDarwinReadPhys((u64.u & ~(uint64_t)PAGE_OFFSET_MASK) | (((uintptr_t)pvPage >> X86_PDPT_SHIFT) & X86_PDPT_MASK_AMD64) * 8, 8, &u64);
+ if (!(u64.u & X86_PDPE_P))
+ {
+ MY_PRINTF("rtR0MemObjDarwinGetPTE: %p -> PDPTE !p\n", pvPage);
+ return 0;
+ }
+ if (u64.u & X86_PDPE_LM_PS)
+ return (u64.u & ~(uint64_t)(_1G -1)) | ((uintptr_t)pvPage & (_1G -1));
+
+ /* PD */
+ rtR0MemObjDarwinReadPhys((u64.u & ~(uint64_t)PAGE_OFFSET_MASK) | (((uintptr_t)pvPage >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK) * 8, 8, &u64);
+ if (!(u64.u & X86_PDE_P))
+ {
+ MY_PRINTF("rtR0MemObjDarwinGetPTE: %p -> PDE !p\n", pvPage);
+ return 0;
+ }
+ if (u64.u & X86_PDE_PS)
+ return (u64.u & ~(uint64_t)(_2M -1)) | ((uintptr_t)pvPage & (_2M -1));
+
+ /* PT */
+ rtR0MemObjDarwinReadPhys((u64.u & ~(uint64_t)PAGE_OFFSET_MASK) | (((uintptr_t)pvPage >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK) * 8, 8, &u64);
+ if (!(u64.u & X86_PTE_P))
+ {
+ MY_PRINTF("rtR0MemObjDarwinGetPTE: %p -> PTE !p\n", pvPage);
+ return 0;
+ }
+ return u64.u;
+ }
+
+ if (fPAE)
+ {
+ /* PDPTR */
+ rtR0MemObjDarwinReadPhys((u64.u & X86_CR3_PAE_PAGE_MASK) | (((uintptr_t)pvPage >> X86_PDPT_SHIFT) & X86_PDPT_MASK_PAE) * 8, 8, &u64);
+ if (!(u64.u & X86_PDE_P))
+ return 0;
+
+ /* PD */
+ rtR0MemObjDarwinReadPhys((u64.u & ~(uint64_t)PAGE_OFFSET_MASK) | (((uintptr_t)pvPage >> X86_PD_PAE_SHIFT) & X86_PD_PAE_MASK) * 8, 8, &u64);
+ if (!(u64.u & X86_PDE_P))
+ return 0;
+ if (u64.u & X86_PDE_PS)
+ return (u64.u & ~(uint64_t)(_2M -1)) | ((uintptr_t)pvPage & (_2M -1));
+
+ /* PT */
+ rtR0MemObjDarwinReadPhys((u64.u & ~(uint64_t)PAGE_OFFSET_MASK) | (((uintptr_t)pvPage >> X86_PT_PAE_SHIFT) & X86_PT_PAE_MASK) * 8, 8, &u64);
+ if (!(u64.u & X86_PTE_P))
+ return 0;
+ return u64.u;
+ }
+
+ /* PD */
+ rtR0MemObjDarwinReadPhys((u64.au32[0] & ~(uint32_t)PAGE_OFFSET_MASK) | (((uintptr_t)pvPage >> X86_PD_SHIFT) & X86_PD_MASK) * 4, 4, &u64);
+ if (!(u64.au32[0] & X86_PDE_P))
+ return 0;
+ if (u64.au32[0] & X86_PDE_PS)
+ return (u64.u & ~(uint64_t)(_2M -1)) | ((uintptr_t)pvPage & (_2M -1));
+
+ /* PT */
+ rtR0MemObjDarwinReadPhys((u64.au32[0] & ~(uint32_t)PAGE_OFFSET_MASK) | (((uintptr_t)pvPage >> X86_PT_SHIFT) & X86_PT_MASK) * 4, 4, &u64);
+ if (!(u64.au32[0] & X86_PTE_P))
+ return 0;
+ return u64.au32[0];
+
+ return 0;
+}
+
+# endif /* unused */
+#endif /* RT_STRICT */
+
+DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)pMem;
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Release the IOMemoryDescriptor or/and IOMemoryMap associated with the object.
+ */
+ if (pMemDarwin->pMemDesc)
+ {
+ pMemDarwin->pMemDesc->complete();
+ pMemDarwin->pMemDesc->release();
+ pMemDarwin->pMemDesc = NULL;
+ }
+
+ if (pMemDarwin->pMemMap)
+ {
+ pMemDarwin->pMemMap->release();
+ pMemDarwin->pMemMap = NULL;
+ }
+
+ /*
+ * Release any memory that we've allocated or locked.
+ */
+ switch (pMemDarwin->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_CONT:
+ break;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ {
+#ifdef USE_VM_MAP_WIRE
+ vm_map_t Map = pMemDarwin->Core.u.Lock.R0Process != NIL_RTR0PROCESS
+ ? get_task_map((task_t)pMemDarwin->Core.u.Lock.R0Process)
+ : kernel_map;
+ kern_return_t kr = vm_map_unwire(Map,
+ (vm_map_offset_t)pMemDarwin->Core.pv,
+ (vm_map_offset_t)pMemDarwin->Core.pv + pMemDarwin->Core.cb,
+ 0 /* not user */);
+ AssertRC(kr == KERN_SUCCESS); /** @todo don't ignore... */
+#endif
+ break;
+ }
+
+ case RTR0MEMOBJTYPE_PHYS:
+ /*if (pMemDarwin->Core.u.Phys.fAllocated)
+ IOFreePhysical(pMemDarwin->Core.u.Phys.PhysBase, pMemDarwin->Core.cb);*/
+ Assert(!pMemDarwin->Core.u.Phys.fAllocated);
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ AssertMsgFailed(("RTR0MEMOBJTYPE_PHYS_NC\n"));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_INTERNAL_ERROR;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ AssertMsgFailed(("RTR0MEMOBJTYPE_RES_VIRT\n"));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_INTERNAL_ERROR;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ /* nothing to do here. */
+ break;
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemDarwin->Core.enmType));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_INTERNAL_ERROR;
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * This is a helper function to executes @a pfnWorker in the context of the
+ * kernel_task
+ *
+ * @returns IPRT status code - result from pfnWorker or dispatching error.
+ * @param pfnWorker The function to call.
+ * @param pArgs The arguments to pass to the function.
+ */
+static int rtR0MemObjDarwinDoInKernelTaskThread(thread_call_func_t pfnWorker, RTR0MEMOBJDARWINTHREADARGS *pArgs)
+{
+ pArgs->rc = VERR_IPE_UNINITIALIZED_STATUS;
+ pArgs->hEvent = NIL_RTSEMEVENTMULTI;
+ int rc = RTSemEventMultiCreate(&pArgs->hEvent);
+ if (RT_SUCCESS(rc))
+ {
+ thread_call_t hCall = thread_call_allocate(pfnWorker, (void *)pArgs);
+ if (hCall)
+ {
+ boolean_t fRc = thread_call_enter(hCall);
+ AssertLogRel(fRc == FALSE);
+
+ rc = RTSemEventMultiWaitEx(pArgs->hEvent, RTSEMWAIT_FLAGS_INDEFINITE | RTSEMWAIT_FLAGS_UNINTERRUPTIBLE,
+ RT_INDEFINITE_WAIT);
+ AssertLogRelRC(rc);
+
+ rc = pArgs->rc;
+ thread_call_free(hCall);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTSemEventMultiDestroy(pArgs->hEvent);
+ }
+ return rc;
+}
+
+
+/**
+ * Signals result to thread waiting in rtR0MemObjDarwinDoInKernelTaskThread.
+ *
+ * @param pArgs The argument structure.
+ * @param rc The IPRT status code to signal.
+ */
+static void rtR0MemObjDarwinSignalThreadWaitinOnTask(RTR0MEMOBJDARWINTHREADARGS volatile *pArgs, int rc)
+{
+ if (ASMAtomicCmpXchgS32(&pArgs->rc, rc, VERR_IPE_UNINITIALIZED_STATUS))
+ {
+ rc = RTSemEventMultiSignal(pArgs->hEvent);
+ AssertLogRelRC(rc);
+ }
+}
+
+
+/**
+ * Kernel memory alloc worker that uses inTaskWithPhysicalMask.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_ADDRESS_TOO_BIG try another way.
+ *
+ * @param ppMem Where to return the memory object.
+ * @param cb The page aligned memory size.
+ * @param fExecutable Whether the mapping needs to be executable.
+ * @param fContiguous Whether the backing memory needs to be contiguous.
+ * @param PhysMask The mask for the backing memory (i.e. range). Use 0 if
+ * you don't care that much or is speculating.
+ * @param MaxPhysAddr The max address to verify the result against. Use
+ * UINT64_MAX if it doesn't matter.
+ * @param enmType The object type.
+ * @param uAlignment The allocation alignment (in bytes).
+ * @param pszTag Allocation tag used for statistics and such.
+ * @param fOnKernelThread Set if we're already on the kernel thread.
+ */
+static int rtR0MemObjNativeAllocWorker(PPRTR0MEMOBJINTERNAL ppMem, size_t cb,
+ bool fExecutable, bool fContiguous,
+ mach_vm_address_t PhysMask, uint64_t MaxPhysAddr,
+ RTR0MEMOBJTYPE enmType, size_t uAlignment, const char *pszTag, bool fOnKernelThread)
+{
+ int rc;
+
+ /*
+ * Because of process code signing properties leaking into kernel space in
+ * in XNU's vm_fault.c code, we have to defer allocations of exec memory to
+ * a thread running in the kernel_task to get consistent results here.
+ *
+ * Trouble strikes in vm_fault_enter() when cs_enforcement_enabled is determined
+ * to be true because current process has the CS_ENFORCEMENT flag, the page flag
+ * vmp_cs_validated is clear, and the protection mask includes VM_PROT_EXECUTE
+ * (pmap_cs_enforced does not apply to macOS it seems). This test seems to go
+ * back to 10.5, though I'm not sure whether it's enabled for macOS that early
+ * on. Only VM_PROT_EXECUTE is problematic for kernel memory, (though
+ * VM_PROT_WRITE on code signed pages is also problematic in theory). As long as
+ * kernel_task doesn't have CS_ENFORCEMENT enabled, we'll be fine switching to it.
+ */
+ if (!fExecutable || fOnKernelThread)
+ { /* likely */ }
+ else
+ {
+ RTR0MEMOBJDARWINALLOCARGS Args;
+ Args.ppMem = ppMem;
+ Args.cb = cb;
+ Args.fExecutable = fExecutable;
+ Args.fContiguous = fContiguous;
+ Args.PhysMask = PhysMask;
+ Args.MaxPhysAddr = MaxPhysAddr;
+ Args.enmType = enmType;
+ Args.uAlignment = uAlignment;
+ Args.pszTag = pszTag;
+ return rtR0MemObjDarwinDoInKernelTaskThread(rtR0MemObjNativeAllockWorkerOnKernelThread, &Args.Core);
+ }
+
+ /*
+ * Try inTaskWithPhysicalMask first, but since we don't quite trust that it
+ * actually respects the physical memory mask (10.5.x is certainly busted),
+ * we'll use rtR0MemObjNativeAllocCont as a fallback for dealing with that.
+ *
+ * The kIOMemoryKernelUserShared flag just forces the result to be page aligned.
+ *
+ * The kIOMemoryMapperNone flag is required since 10.8.2 (IOMMU changes?).
+ */
+
+ /* This is an old fudge from the snow leoard days: "Is it only on snow leopard?
+ Seen allocating memory for the VM structure, last page corrupted or
+ inaccessible." Made it only apply to snow leopard and older for now. */
+ size_t cbFudged = cb;
+ if (version_major >= 11 /* 10 = 10.7.x = Lion. */)
+ { /* likely */ }
+ else
+ cbFudged += PAGE_SIZE;
+
+ IOOptionBits fOptions = kIOMemoryKernelUserShared | kIODirectionInOut;
+ if (fContiguous)
+ {
+ fOptions |= kIOMemoryPhysicallyContiguous;
+ if ( version_major > 12
+ || (version_major == 12 && version_minor >= 2) /* 10.8.2 = Mountain Kitten */ )
+ fOptions |= kIOMemoryHostPhysicallyContiguous; /* (Just to make ourselves clear, in case the xnu code changes.) */
+ }
+ if (version_major >= 12 /* 12 = 10.8.x = Mountain Kitten */)
+ fOptions |= kIOMemoryMapperNone;
+
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 && 0 /* enable when/if necessary */
+ /* Paranoia: Don't misrepresent our intentions, we won't map kernel executable memory into ring-0. */
+ if (fExecutable && version_major >= 11 /* 10.7.x = Lion, as below */)
+ {
+ fOptions &= ~kIOMemoryKernelUserShared;
+ if (uAlignment < PAGE_SIZE)
+ uAlignment = PAGE_SIZE;
+ }
+#endif
+
+ /* The public initWithPhysicalMask virtual method appeared in 10.7.0, in
+ versions 10.5.0 up to 10.7.0 it was private, and 10.4.8-10.5.0 it was
+ x86 only and didn't have the alignment parameter (slot was different too). */
+ uint64_t uAlignmentActual = uAlignment;
+ IOBufferMemoryDescriptor *pMemDesc;
+#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+ if (version_major >= 11 /* 11 = 10.7.x = Lion, could probably allow 10.5.0+ here if we really wanted to. */)
+ {
+ /* Starting with 10.6.x the physical mask is ignored if alignment is higher
+ than 1. The assumption seems to be that inTaskWithPhysicalMask() should
+ be used and the alignment inferred from the PhysMask argument. */
+ if (MaxPhysAddr != UINT64_MAX)
+ {
+ Assert(RT_ALIGN_64(PhysMask, uAlignment) == PhysMask);
+ uAlignmentActual = 1;
+ }
+
+ pMemDesc = new IOBufferMemoryDescriptor;
+ if (pMemDesc)
+ {
+ if (pMemDesc->initWithPhysicalMask(kernel_task, fOptions, cbFudged, uAlignmentActual, PhysMask))
+ { /* likely */ }
+ else
+ {
+ pMemDesc->release();
+ pMemDesc = NULL;
+ }
+ }
+ }
+ else
+#endif
+ pMemDesc = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(kernel_task, fOptions, cbFudged, PhysMask);
+ if (pMemDesc)
+ {
+ IOReturn IORet = pMemDesc->prepare(kIODirectionInOut);
+ if (IORet == kIOReturnSuccess)
+ {
+ void *pv = pMemDesc->getBytesNoCopy(0, cbFudged);
+ if (pv)
+ {
+ /*
+ * Check if it's all below 4GB.
+ */
+ addr64_t AddrPrev = 0;
+ MaxPhysAddr &= ~(uint64_t)PAGE_OFFSET_MASK;
+ for (IOByteCount off = 0; off < cb; off += PAGE_SIZE)
+ {
+#ifdef __LP64__
+ addr64_t Addr = pMemDesc->getPhysicalSegment(off, NULL, kIOMemoryMapperNone);
+#else
+ addr64_t Addr = pMemDesc->getPhysicalSegment64(off, NULL);
+#endif
+ if ( Addr > MaxPhysAddr
+ || !Addr
+ || (Addr & PAGE_OFFSET_MASK)
+ || ( fContiguous
+ && !off
+ && Addr == AddrPrev + PAGE_SIZE))
+ {
+ /* Buggy API, try allocate the memory another way. */
+ pMemDesc->complete();
+ pMemDesc->release();
+ if (PhysMask)
+ {
+ kprintf("rtR0MemObjNativeAllocWorker: off=%zx Addr=%llx AddrPrev=%llx MaxPhysAddr=%llx PhysMas=%llx fContiguous=%d fOptions=%#x - buggy API!\n",
+ (size_t)off, Addr, AddrPrev, MaxPhysAddr, PhysMask, fContiguous, fOptions);
+ LogRel(("rtR0MemObjNativeAllocWorker: off=%zx Addr=%llx AddrPrev=%llx MaxPhysAddr=%llx PhysMas=%llx fContiguous=%RTbool fOptions=%#x - buggy API!\n",
+ (size_t)off, Addr, AddrPrev, MaxPhysAddr, PhysMask, fContiguous, fOptions));
+ }
+ return VERR_ADDRESS_TOO_BIG;
+ }
+ AddrPrev = Addr;
+ }
+
+ /*
+ * Check that it's aligned correctly.
+ */
+ if ((uintptr_t)pv & (uAlignment - 1))
+ {
+ pMemDesc->complete();
+ pMemDesc->release();
+ if (PhysMask)
+ {
+ kprintf("rtR0MemObjNativeAllocWorker: pv=%p uAlignment=%#zx (MaxPhysAddr=%llx PhysMas=%llx fContiguous=%d fOptions=%#x) - buggy API!!\n",
+ pv, uAlignment, MaxPhysAddr, PhysMask, fContiguous, fOptions);
+ LogRel(("rtR0MemObjNativeAllocWorker: pv=%p uAlignment=%#zx (MaxPhysAddr=%llx PhysMas=%llx fContiguous=%RTbool fOptions=%#x) - buggy API!\n",
+ pv, uAlignment, MaxPhysAddr, PhysMask, fContiguous, fOptions));
+ }
+ return VERR_NOT_SUPPORTED;
+ }
+
+#ifdef RT_STRICT
+ /* check that the memory is actually mapped. */
+ //addr64_t Addr = pMemDesc->getPhysicalSegment64(0, NULL);
+ //printf("rtR0MemObjNativeAllocWorker: pv=%p %8llx %8llx\n", pv, rtR0MemObjDarwinGetPTE(pv), Addr);
+ RTTHREADPREEMPTSTATE State = RTTHREADPREEMPTSTATE_INITIALIZER;
+ RTThreadPreemptDisable(&State);
+ rtR0MemObjDarwinTouchPages(pv, cb);
+ RTThreadPreemptRestore(&State);
+#endif
+
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)rtR0MemObjNew(sizeof(*pMemDarwin), enmType, pv, cb, pszTag);
+ if (pMemDarwin)
+ {
+ if (fOptions & kIOMemoryKernelUserShared)
+ pMemDarwin->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ else
+ pMemDarwin->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ if (fContiguous)
+ {
+#ifdef __LP64__
+ addr64_t PhysBase64 = pMemDesc->getPhysicalSegment(0, NULL, kIOMemoryMapperNone);
+#else
+ addr64_t PhysBase64 = pMemDesc->getPhysicalSegment64(0, NULL);
+#endif
+ RTHCPHYS PhysBase = PhysBase64; Assert(PhysBase == PhysBase64);
+ if (enmType == RTR0MEMOBJTYPE_CONT)
+ pMemDarwin->Core.u.Cont.Phys = PhysBase;
+ else if (enmType == RTR0MEMOBJTYPE_PHYS)
+ pMemDarwin->Core.u.Phys.PhysBase = PhysBase;
+ else
+ AssertMsgFailed(("enmType=%d\n", enmType));
+ }
+
+ if (fExecutable)
+ {
+ rc = rtR0MemObjNativeProtectWorker(&pMemDarwin->Core, 0, cb,
+ RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC);
+#ifdef RT_STRICT
+ if (RT_SUCCESS(rc))
+ {
+ /* check that the memory is actually mapped. */
+ RTTHREADPREEMPTSTATE State2 = RTTHREADPREEMPTSTATE_INITIALIZER;
+ RTThreadPreemptDisable(&State2);
+ rtR0MemObjDarwinTouchPages(pv, cb);
+ RTThreadPreemptRestore(&State2);
+ }
+#endif
+ /* Bug 6226: Ignore KERN_PROTECTION_FAILURE on Leopard and older. */
+ if ( rc == VERR_PERMISSION_DENIED
+ && version_major <= 10 /* 10 = 10.6.x = Snow Leopard. */)
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ pMemDarwin->pMemDesc = pMemDesc;
+ *ppMem = &pMemDarwin->Core;
+ return VINF_SUCCESS;
+ }
+
+ rtR0MemObjDelete(&pMemDarwin->Core);
+ }
+
+ if (enmType == RTR0MEMOBJTYPE_PHYS_NC)
+ rc = VERR_NO_PHYS_MEMORY;
+ else if (enmType == RTR0MEMOBJTYPE_LOW)
+ rc = VERR_NO_LOW_MEMORY;
+ else if (enmType == RTR0MEMOBJTYPE_CONT)
+ rc = VERR_NO_CONT_MEMORY;
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_MEMOBJ_INIT_FAILED;
+
+ pMemDesc->complete();
+ }
+ else
+ rc = RTErrConvertFromDarwinIO(IORet);
+ pMemDesc->release();
+ }
+ else
+ rc = VERR_MEMOBJ_INIT_FAILED;
+ Assert(rc != VERR_ADDRESS_TOO_BIG);
+ return rc;
+}
+
+
+/**
+ * rtR0MemObjNativeAllocWorker kernel_task wrapper function.
+ */
+static void rtR0MemObjNativeAllockWorkerOnKernelThread(void *pvUser0, void *pvUser1)
+{
+ AssertPtr(pvUser0); Assert(pvUser1 == NULL); NOREF(pvUser1);
+ RTR0MEMOBJDARWINALLOCARGS volatile *pArgs = (RTR0MEMOBJDARWINALLOCARGS volatile *)pvUser0;
+ int rc = rtR0MemObjNativeAllocWorker(pArgs->ppMem, pArgs->cb, pArgs->fExecutable, pArgs->fContiguous, pArgs->PhysMask,
+ pArgs->MaxPhysAddr, pArgs->enmType, pArgs->uAlignment, pArgs->pszTag,
+ true /*fOnKernelThread*/);
+ rtR0MemObjDarwinSignalThreadWaitinOnTask(&pArgs->Core, rc);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ int rc = rtR0MemObjNativeAllocWorker(ppMem, cb, fExecutable, false /* fContiguous */, 0 /* PhysMask */, UINT64_MAX,
+ RTR0MEMOBJTYPE_PAGE, PAGE_SIZE, pszTag, false /*fOnKernelThread*/);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Try IOMallocPhysical/IOMallocAligned first.
+ * Then try optimistically without a physical address mask, which will always
+ * end up using IOMallocAligned.
+ *
+ * (See bug comment in the worker and IOBufferMemoryDescriptor::initWithPhysicalMask.)
+ */
+ int rc = rtR0MemObjNativeAllocWorker(ppMem, cb, fExecutable, false /* fContiguous */, ~(uint32_t)PAGE_OFFSET_MASK,
+ _4G - PAGE_SIZE, RTR0MEMOBJTYPE_LOW, PAGE_SIZE, pszTag, false /*fOnKernelThread*/);
+ if (rc == VERR_ADDRESS_TOO_BIG)
+ rc = rtR0MemObjNativeAllocWorker(ppMem, cb, fExecutable, false /* fContiguous */, 0 /* PhysMask */,
+ _4G - PAGE_SIZE, RTR0MEMOBJTYPE_LOW, PAGE_SIZE, pszTag, false /*fOnKernelThread*/);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ int rc = rtR0MemObjNativeAllocWorker(ppMem, cb, fExecutable, true /* fContiguous */,
+ ~(uint32_t)PAGE_OFFSET_MASK, _4G - PAGE_SIZE,
+ RTR0MEMOBJTYPE_CONT, PAGE_SIZE, pszTag, false /*fOnKernelThread*/);
+
+ /*
+ * Workaround for bogus IOKernelAllocateContiguous behavior, just in case.
+ * cb <= PAGE_SIZE allocations take a different path, using a different allocator.
+ */
+ if (RT_FAILURE(rc) && cb <= PAGE_SIZE)
+ rc = rtR0MemObjNativeAllocWorker(ppMem, cb + PAGE_SIZE, fExecutable, true /* fContiguous */,
+ ~(uint32_t)PAGE_OFFSET_MASK, _4G - PAGE_SIZE,
+ RTR0MEMOBJTYPE_CONT, PAGE_SIZE, pszTag, false /*fOnKernelThread*/);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment,
+ const char *pszTag)
+{
+ if (uAlignment != PAGE_SIZE)
+ {
+ /* See rtR0MemObjNativeAllocWorker: */
+ if (version_major < 9 /* 9 = 10.5.x = Snow Leopard */)
+ return VERR_NOT_SUPPORTED;
+ }
+
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Translate the PhysHighest address into a mask.
+ */
+ int rc;
+ if (PhysHighest == NIL_RTHCPHYS)
+ rc = rtR0MemObjNativeAllocWorker(ppMem, cb, false /* fExecutable */, true /* fContiguous */,
+ uAlignment <= PAGE_SIZE ? 0 : ~(mach_vm_address_t)(uAlignment - 1) /* PhysMask*/,
+ UINT64_MAX, RTR0MEMOBJTYPE_PHYS, uAlignment, pszTag, false /*fOnKernelThread*/);
+ else
+ {
+ mach_vm_address_t PhysMask = 0;
+ PhysMask = ~(mach_vm_address_t)0;
+ while (PhysMask > (PhysHighest | PAGE_OFFSET_MASK))
+ PhysMask >>= 1;
+ AssertReturn(PhysMask + 1 <= cb, VERR_INVALID_PARAMETER);
+ PhysMask &= ~(mach_vm_address_t)(uAlignment - 1);
+
+ rc = rtR0MemObjNativeAllocWorker(ppMem, cb, false /* fExecutable */, true /* fContiguous */,
+ PhysMask, PhysHighest,
+ RTR0MEMOBJTYPE_PHYS, uAlignment, pszTag, false /*fOnKernelThread*/);
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ /** @todo rtR0MemObjNativeAllocPhys / darwin.
+ * This might be a bit problematic and may very well require having to create our own
+ * object which we populate with pages but without mapping it into any address space.
+ * Estimate is 2-3 days.
+ */
+ RT_NOREF(ppMem, cb, PhysHighest, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy,
+ const char *pszTag)
+{
+ AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE, VERR_NOT_SUPPORTED);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Create a descriptor for it (the validation is always true on intel macs, but
+ * as it doesn't harm us keep it in).
+ */
+ int rc = VERR_ADDRESS_TOO_BIG;
+ IOAddressRange aRanges[1] = { { Phys, cb } };
+ if ( aRanges[0].address == Phys
+ && aRanges[0].length == cb)
+ {
+ IOMemoryDescriptor *pMemDesc = IOMemoryDescriptor::withAddressRanges(&aRanges[0], RT_ELEMENTS(aRanges),
+ kIODirectionInOut, NULL /*task*/);
+ if (pMemDesc)
+ {
+#ifdef __LP64__
+ Assert(Phys == pMemDesc->getPhysicalSegment(0, NULL, kIOMemoryMapperNone));
+#else
+ Assert(Phys == pMemDesc->getPhysicalSegment64(0, NULL));
+#endif
+
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)rtR0MemObjNew(sizeof(*pMemDarwin), RTR0MEMOBJTYPE_PHYS,
+ NULL, cb, pszTag);
+ if (pMemDarwin)
+ {
+ pMemDarwin->Core.u.Phys.PhysBase = Phys;
+ pMemDarwin->Core.u.Phys.fAllocated = false;
+ pMemDarwin->Core.u.Phys.uCachePolicy = uCachePolicy;
+ pMemDarwin->pMemDesc = pMemDesc;
+ *ppMem = &pMemDarwin->Core;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ pMemDesc->release();
+ }
+ else
+ rc = VERR_MEMOBJ_INIT_FAILED;
+ }
+ else
+ AssertMsgFailed(("%#llx %llx\n", (unsigned long long)Phys, (unsigned long long)cb));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+/**
+ * Internal worker for locking down pages.
+ *
+ * @return IPRT status code.
+ *
+ * @param ppMem Where to store the memory object pointer.
+ * @param pv First page.
+ * @param cb Number of bytes.
+ * @param fAccess The desired access, a combination of RTMEM_PROT_READ
+ * and RTMEM_PROT_WRITE.
+ * @param Task The task \a pv and \a cb refers to.
+ * @param pszTag Allocation tag used for statistics and such.
+ */
+static int rtR0MemObjNativeLock(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, task_t Task,
+ const char *pszTag)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+ NOREF(fAccess);
+#ifdef USE_VM_MAP_WIRE
+ vm_map_t Map = get_task_map(Task);
+ Assert(Map);
+
+ /*
+ * First try lock the memory.
+ */
+ int rc = VERR_LOCK_FAILED;
+ kern_return_t kr = vm_map_wire(get_task_map(Task),
+ (vm_map_offset_t)pv,
+ (vm_map_offset_t)pv + cb,
+ VM_PROT_DEFAULT,
+ 0 /* not user */);
+ if (kr == KERN_SUCCESS)
+ {
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)rtR0MemObjNew(sizeof(*pMemDarwin), RTR0MEMOBJTYPE_LOCK, pv, cb, pszTag);
+ if (pMemDarwin)
+ {
+ pMemDarwin->Core.u.Lock.R0Process = (RTR0PROCESS)Task;
+ *ppMem = &pMemDarwin->Core;
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ kr = vm_map_unwire(get_task_map(Task), (vm_map_offset_t)pv, (vm_map_offset_t)pv + cb, 0 /* not user */);
+ Assert(kr == KERN_SUCCESS);
+ rc = VERR_NO_MEMORY;
+ }
+
+#else
+
+ /*
+ * Create a descriptor and try lock it (prepare).
+ */
+ int rc = VERR_MEMOBJ_INIT_FAILED;
+ IOMemoryDescriptor *pMemDesc = IOMemoryDescriptor::withAddressRange((vm_address_t)pv, cb, kIODirectionInOut, Task);
+ if (pMemDesc)
+ {
+ IOReturn IORet = pMemDesc->prepare(kIODirectionInOut);
+ if (IORet == kIOReturnSuccess)
+ {
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)rtR0MemObjNew(sizeof(*pMemDarwin), RTR0MEMOBJTYPE_LOCK,
+ pv, cb, pszTag);
+ if (pMemDarwin)
+ {
+ pMemDarwin->Core.u.Lock.R0Process = (RTR0PROCESS)Task;
+ pMemDarwin->pMemDesc = pMemDesc;
+ *ppMem = &pMemDarwin->Core;
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ pMemDesc->complete();
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_LOCK_FAILED;
+ pMemDesc->release();
+ }
+#endif
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ return rtR0MemObjNativeLock(ppMem, (void *)R3Ptr, cb, fAccess, (task_t)R0Process, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ return rtR0MemObjNativeLock(ppMem, pv, cb, fAccess, kernel_task, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ const char *pszTag)
+{
+ RT_NOREF(ppMem, pvFixed, cb, uAlignment, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ RT_NOREF(ppMem, R3PtrFixed, cb, uAlignment, R0Process, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ RT_NOREF(fProt);
+ AssertReturn(pvFixed == (void *)-1, VERR_NOT_SUPPORTED);
+
+ /*
+ * Check that the specified alignment is supported.
+ */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+ Assert(!offSub || cbSub);
+
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Must have a memory descriptor that we can map.
+ */
+ int rc = VERR_INVALID_PARAMETER;
+ PRTR0MEMOBJDARWIN pMemToMapDarwin = (PRTR0MEMOBJDARWIN)pMemToMap;
+ if (pMemToMapDarwin->pMemDesc)
+ {
+ /* The kIOMapPrefault option was added in 10.10.0; causes PTEs to be populated with
+ INTEL_PTE_WIRED to be set, just like we desire (see further down). However, till
+ 10.13.0 it was not available for use on kernel mappings. Oh, fudge. */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ static uint32_t volatile s_fOptions = UINT32_MAX;
+ uint32_t fOptions = s_fOptions;
+ if (RT_UNLIKELY(fOptions == UINT32_MAX))
+ s_fOptions = fOptions = version_major >= 17 ? 0x10000000 /*kIOMapPrefault*/ : 0; /* Since 10.13.0 (High Sierra). */
+
+ IOMemoryMap *pMemMap = pMemToMapDarwin->pMemDesc->createMappingInTask(kernel_task,
+ 0,
+ kIOMapAnywhere | kIOMapDefaultCache | fOptions,
+ offSub,
+ cbSub);
+#else
+ IOMemoryMap *pMemMap = pMemToMapDarwin->pMemDesc->map(kernel_task,
+ 0,
+ kIOMapAnywhere | kIOMapDefaultCache,
+ offSub,
+ cbSub);
+#endif
+ if (pMemMap)
+ {
+ IOVirtualAddress VirtAddr = pMemMap->getVirtualAddress();
+ void *pv = (void *)(uintptr_t)VirtAddr;
+ if ((uintptr_t)pv == VirtAddr && pv != NULL)
+ {
+//#ifdef __LP64__
+// addr64_t Addr = pMemToMapDarwin->pMemDesc->getPhysicalSegment(offSub, NULL, kIOMemoryMapperNone);
+//#else
+// addr64_t Addr = pMemToMapDarwin->pMemDesc->getPhysicalSegment64(offSub, NULL);
+//#endif
+// MY_PRINTF("pv=%p: %8llx %8llx\n", pv, rtR0MemObjDarwinGetPTE(pv), Addr);
+
+// /*
+// * Explicitly lock it so that we're sure it is present and that
+// * its PTEs cannot be recycled.
+// * Note! withAddressRange() doesn't work as it adds kIOMemoryTypeVirtual64
+// * to the options which causes prepare() to not wire the pages.
+// * This is probably a bug.
+// */
+// IOAddressRange Range = { (mach_vm_address_t)pv, cbSub };
+// IOMemoryDescriptor *pMemDesc = IOMemoryDescriptor::withOptions(&Range,
+// 1 /* count */,
+// 0 /* offset */,
+// kernel_task,
+// kIODirectionInOut | kIOMemoryTypeVirtual,
+// kIOMapperSystem);
+// if (pMemDesc)
+// {
+// IOReturn IORet = pMemDesc->prepare(kIODirectionInOut);
+// if (IORet == kIOReturnSuccess)
+// {
+ /* HACK ALERT! On kernels older than 10.10 (xnu version 14), we need to fault in
+ the pages here so they can safely be accessed from inside simple
+ locks and when preemption is disabled (no page-ins allowed).
+ Note! This touching does not cause INTEL_PTE_WIRED (bit 10) to be set as we go
+ thru general #PF and vm_fault doesn't figure it should be wired or something. */
+ rtR0MemObjDarwinTouchPages(pv, cbSub ? cbSub : pMemToMap->cb);
+ /** @todo First, the memory should've been mapped by now, and second, it
+ * should have the wired attribute in the PTE (bit 10). Neither seems to
+ * be the case. The disabled locking code doesn't make any difference,
+ * which is extremely odd, and breaks rtR0MemObjNativeGetPagePhysAddr
+ * (getPhysicalSegment64 -> 64 for the lock descriptor. */
+//#ifdef __LP64__
+// addr64_t Addr2 = pMemToMapDarwin->pMemDesc->getPhysicalSegment(offSub, NULL, kIOMemoryMapperNone);
+//#else
+// addr64_t Addr2 = pMemToMapDarwin->pMemDesc->getPhysicalSegment64(offSub, NULL);
+//#endif
+// MY_PRINTF("pv=%p: %8llx %8llx (%d)\n", pv, rtR0MemObjDarwinGetPTE(pv), Addr2, 2);
+
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)rtR0MemObjNew(sizeof(*pMemDarwin), RTR0MEMOBJTYPE_MAPPING,
+ pv, cbSub ? cbSub : pMemToMap->cb, pszTag);
+ if (pMemDarwin)
+ {
+ pMemDarwin->Core.u.Mapping.R0Process = NIL_RTR0PROCESS;
+ pMemDarwin->pMemMap = pMemMap;
+// pMemDarwin->pMemDesc = pMemDesc;
+ *ppMem = &pMemDarwin->Core;
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+// pMemDesc->complete();
+// rc = VERR_NO_MEMORY;
+// }
+// else
+// rc = RTErrConvertFromDarwinIO(IORet);
+// pMemDesc->release();
+// }
+// else
+// rc = VERR_MEMOBJ_INIT_FAILED;
+ }
+ else if (pv)
+ rc = VERR_ADDRESS_TOO_BIG;
+ else
+ rc = VERR_MAP_FAILED;
+ pMemMap->release();
+ }
+ else
+ rc = VERR_MAP_FAILED;
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ RT_NOREF(fProt);
+
+ /*
+ * Check for unsupported things.
+ */
+ AssertReturn(R3PtrFixed == (RTR3PTR)-1, VERR_NOT_SUPPORTED);
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+ Assert(!offSub || cbSub);
+
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Must have a memory descriptor.
+ */
+ int rc = VERR_INVALID_PARAMETER;
+ PRTR0MEMOBJDARWIN pMemToMapDarwin = (PRTR0MEMOBJDARWIN)pMemToMap;
+ if (pMemToMapDarwin->pMemDesc)
+ {
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101000 /* The kIOMapPrefault option was added in 10.10.0. */
+ IOMemoryMap *pMemMap = pMemToMapDarwin->pMemDesc->createMappingInTask((task_t)R0Process,
+ 0,
+ kIOMapAnywhere | kIOMapDefaultCache | kIOMapPrefault,
+ offSub,
+ cbSub);
+#elif MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ static uint32_t volatile s_fOptions = UINT32_MAX;
+ uint32_t fOptions = s_fOptions;
+ if (RT_UNLIKELY(fOptions == UINT32_MAX))
+ s_fOptions = fOptions = version_major >= 14 ? 0x10000000 /*kIOMapPrefault*/ : 0; /* Since 10.10.0. */
+ IOMemoryMap *pMemMap = pMemToMapDarwin->pMemDesc->createMappingInTask((task_t)R0Process,
+ 0,
+ kIOMapAnywhere | kIOMapDefaultCache | fOptions,
+ offSub,
+ cbSub);
+#else
+ IOMemoryMap *pMemMap = pMemToMapDarwin->pMemDesc->map((task_t)R0Process,
+ 0,
+ kIOMapAnywhere | kIOMapDefaultCache,
+ offSub,
+ cbSub);
+#endif
+ if (pMemMap)
+ {
+ IOVirtualAddress VirtAddr = pMemMap->getVirtualAddress();
+ void *pv = (void *)(uintptr_t)VirtAddr;
+ if ((uintptr_t)pv == VirtAddr && pv != NULL)
+ {
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)rtR0MemObjNew(sizeof(*pMemDarwin), RTR0MEMOBJTYPE_MAPPING,
+ pv, cbSub ? cbSub : pMemToMap->cb, pszTag);
+ if (pMemDarwin)
+ {
+ pMemDarwin->Core.u.Mapping.R0Process = R0Process;
+ pMemDarwin->pMemMap = pMemMap;
+ *ppMem = &pMemDarwin->Core;
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ }
+ else if (pv)
+ rc = VERR_ADDRESS_TOO_BIG;
+ else
+ rc = VERR_MAP_FAILED;
+ pMemMap->release();
+ }
+ else
+ rc = VERR_MAP_FAILED;
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+/**
+ * Worker for rtR0MemObjNativeProtect that's typically called in a different
+ * context.
+ */
+static int rtR0MemObjNativeProtectWorker(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /* Get the map for the object. */
+ vm_map_t pVmMap = rtR0MemObjDarwinGetMap(pMem);
+ if (!pVmMap)
+ {
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Convert the protection.
+ */
+ vm_prot_t fMachProt;
+ switch (fProt)
+ {
+ case RTMEM_PROT_NONE:
+ fMachProt = VM_PROT_NONE;
+ break;
+ case RTMEM_PROT_READ:
+ fMachProt = VM_PROT_READ;
+ break;
+ case RTMEM_PROT_READ | RTMEM_PROT_WRITE:
+ fMachProt = VM_PROT_READ | VM_PROT_WRITE;
+ break;
+ case RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC:
+ fMachProt = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
+ break;
+ case RTMEM_PROT_WRITE:
+ fMachProt = VM_PROT_WRITE | VM_PROT_READ; /* never write-only */
+ break;
+ case RTMEM_PROT_WRITE | RTMEM_PROT_EXEC:
+ fMachProt = VM_PROT_WRITE | VM_PROT_EXECUTE | VM_PROT_READ; /* never write-only or execute-only */
+ break;
+ case RTMEM_PROT_EXEC:
+ fMachProt = VM_PROT_EXECUTE | VM_PROT_READ; /* never execute-only */
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Do the job.
+ */
+ vm_offset_t Start = (uintptr_t)pMem->pv + offSub;
+ kern_return_t krc = vm_protect(pVmMap,
+ Start,
+ cbSub,
+ false,
+ fMachProt);
+ if (krc != KERN_SUCCESS)
+ {
+ static int s_cComplaints = 0;
+ if (s_cComplaints < 10)
+ {
+ s_cComplaints++;
+ printf("rtR0MemObjNativeProtect: vm_protect(%p,%p,%p,false,%#x) -> %d\n",
+ (void *)pVmMap, (void *)Start, (void *)cbSub, fMachProt, krc);
+
+ kern_return_t krc2;
+ vm_offset_t pvReal = Start;
+ vm_size_t cbReal = 0;
+ mach_msg_type_number_t cInfo = VM_REGION_BASIC_INFO_COUNT;
+ struct vm_region_basic_info Info;
+ RT_ZERO(Info);
+ krc2 = vm_region(pVmMap, &pvReal, &cbReal, VM_REGION_BASIC_INFO, (vm_region_info_t)&Info, &cInfo, NULL);
+ printf("rtR0MemObjNativeProtect: basic info - krc2=%d pv=%p cb=%p prot=%#x max=%#x inh=%#x shr=%d rvd=%d off=%#x behavior=%#x wired=%#x\n",
+ krc2, (void *)pvReal, (void *)cbReal, Info.protection, Info.max_protection, Info.inheritance,
+ Info.shared, Info.reserved, Info.offset, Info.behavior, Info.user_wired_count);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return RTErrConvertFromDarwinKern(krc);
+ }
+
+ /*
+ * Touch the pages if they should be writable afterwards and accessible
+ * from code which should never fault. vm_protect() may leave pages
+ * temporarily write protected, possibly due to pmap no-upgrade rules?
+ *
+ * This is the same trick (or HACK ALERT if you like) as applied in
+ * rtR0MemObjNativeMapKernel.
+ */
+ if ( pMem->enmType != RTR0MEMOBJTYPE_MAPPING
+ || pMem->u.Mapping.R0Process == NIL_RTR0PROCESS)
+ {
+ if (fProt & RTMEM_PROT_WRITE)
+ rtR0MemObjDarwinTouchPages((void *)Start, cbSub);
+ /*
+ * Sniff (read) read-only pages too, just to be sure.
+ */
+ else if (fProt & (RTMEM_PROT_READ | RTMEM_PROT_EXEC))
+ rtR0MemObjDarwinSniffPages((void const *)Start, cbSub);
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * rtR0MemObjNativeProtect kernel_task wrapper function.
+ */
+static void rtR0MemObjNativeProtectWorkerOnKernelThread(void *pvUser0, void *pvUser1)
+{
+ AssertPtr(pvUser0); Assert(pvUser1 == NULL); NOREF(pvUser1);
+ RTR0MEMOBJDARWINPROTECTARGS *pArgs = (RTR0MEMOBJDARWINPROTECTARGS *)pvUser0;
+ int rc = rtR0MemObjNativeProtectWorker(pArgs->pMem, pArgs->offSub, pArgs->cbSub, pArgs->fProt);
+ rtR0MemObjDarwinSignalThreadWaitinOnTask(&pArgs->Core, rc);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ /*
+ * The code won't work right because process codesigning properties leaks
+ * into kernel_map memory management. So, if the user process we're running
+ * in has CS restrictions active, we cannot play around with the EXEC
+ * protection because some vm_fault.c think we're modifying the process map
+ * or something.
+ */
+ int rc;
+ if (rtR0MemObjDarwinGetMap(pMem) == kernel_map)
+ {
+ RTR0MEMOBJDARWINPROTECTARGS Args;
+ Args.pMem = pMem;
+ Args.offSub = offSub;
+ Args.cbSub = cbSub;
+ Args.fProt = fProt;
+ rc = rtR0MemObjDarwinDoInKernelTaskThread(rtR0MemObjNativeProtectWorkerOnKernelThread, &Args.Core);
+ }
+ else
+ rc = rtR0MemObjNativeProtectWorker(pMem, offSub, cbSub, fProt);
+ return rc;
+}
+
+
+DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ RTHCPHYS PhysAddr;
+ PRTR0MEMOBJDARWIN pMemDarwin = (PRTR0MEMOBJDARWIN)pMem;
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+#ifdef USE_VM_MAP_WIRE
+ /*
+ * Locked memory doesn't have a memory descriptor and
+ * needs to be handled differently.
+ */
+ if (pMemDarwin->Core.enmType == RTR0MEMOBJTYPE_LOCK)
+ {
+ ppnum_t PgNo;
+ if (pMemDarwin->Core.u.Lock.R0Process == NIL_RTR0PROCESS)
+ PgNo = pmap_find_phys(kernel_pmap, (uintptr_t)pMemDarwin->Core.pv + iPage * PAGE_SIZE);
+ else
+ {
+ /*
+ * From what I can tell, Apple seems to have locked up the all the
+ * available interfaces that could help us obtain the pmap_t of a task
+ * or vm_map_t.
+
+ * So, we'll have to figure out where in the vm_map_t structure it is
+ * and read it our selves. ASSUMING that kernel_pmap is pointed to by
+ * kernel_map->pmap, we scan kernel_map to locate the structure offset.
+ * Not nice, but it will hopefully do the job in a reliable manner...
+ *
+ * (get_task_pmap, get_map_pmap or vm_map_pmap is what we really need btw.)
+ */
+ static int s_offPmap = -1;
+ if (RT_UNLIKELY(s_offPmap == -1))
+ {
+ pmap_t const *p = (pmap_t *)kernel_map;
+ pmap_t const * const pEnd = p + 64;
+ for (; p < pEnd; p++)
+ if (*p == kernel_pmap)
+ {
+ s_offPmap = (uintptr_t)p - (uintptr_t)kernel_map;
+ break;
+ }
+ AssertReturn(s_offPmap >= 0, NIL_RTHCPHYS);
+ }
+ pmap_t Pmap = *(pmap_t *)((uintptr_t)get_task_map((task_t)pMemDarwin->Core.u.Lock.R0Process) + s_offPmap);
+ PgNo = pmap_find_phys(Pmap, (uintptr_t)pMemDarwin->Core.pv + iPage * PAGE_SIZE);
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ AssertReturn(PgNo, NIL_RTHCPHYS);
+ PhysAddr = (RTHCPHYS)PgNo << PAGE_SHIFT;
+ Assert((PhysAddr >> PAGE_SHIFT) == PgNo);
+ }
+ else
+#endif /* USE_VM_MAP_WIRE */
+ {
+ /*
+ * Get the memory descriptor.
+ */
+ IOMemoryDescriptor *pMemDesc = pMemDarwin->pMemDesc;
+ if (!pMemDesc)
+ pMemDesc = pMemDarwin->pMemMap->getMemoryDescriptor();
+ AssertReturn(pMemDesc, NIL_RTHCPHYS);
+
+ /*
+ * If we've got a memory descriptor, use getPhysicalSegment64().
+ */
+#ifdef __LP64__
+ addr64_t Addr = pMemDesc->getPhysicalSegment(iPage * PAGE_SIZE, NULL, kIOMemoryMapperNone);
+#else
+ addr64_t Addr = pMemDesc->getPhysicalSegment64(iPage * PAGE_SIZE, NULL);
+#endif
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ AssertMsgReturn(Addr, ("iPage=%u\n", iPage), NIL_RTHCPHYS);
+ PhysAddr = Addr;
+ AssertMsgReturn(PhysAddr == Addr, ("PhysAddr=%RHp Addr=%RX64\n", PhysAddr, (uint64_t)Addr), NIL_RTHCPHYS);
+ }
+
+ return PhysAddr;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/memuserkernel-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/memuserkernel-r0drv-darwin.cpp
new file mode 100644
index 00000000..107d4f93
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/memuserkernel-r0drv-darwin.cpp
@@ -0,0 +1,128 @@
+/* $Id: memuserkernel-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - User & Kernel Memory, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/errcore.h>
+
+
+RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb)
+{
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+ int rc = copyin((const user_addr_t)R3PtrSrc, pvDst, cb);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb)
+{
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+ int rc = copyout(pvSrc, R3PtrDst, cb);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr)
+{
+ /* the commpage is above this. */
+#ifdef RT_ARCH_X86
+ return R3Ptr < VM_MAX_ADDRESS;
+#else
+ return R3Ptr < VM_MAX_PAGE_ADDRESS;
+#endif
+}
+
+
+RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv)
+{
+ /* Found no public #define or symbol for checking this, so we'll
+ have to make do with thing found in the debugger and the sources. */
+#ifdef RT_ARCH_X86
+ NOREF(pv);
+ return true; /* Almost anything is a valid kernel address here. */
+
+#elif defined(RT_ARCH_AMD64)
+ return (uintptr_t)pv >= UINT64_C(0xffff800000000000);
+
+#else
+# error "PORTME"
+#endif
+}
+
+
+RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void)
+{
+ /* As mentioned in RTR0MemKernelIsValidAddr, found no way of checking
+ this at compiler or runtime. */
+#ifdef RT_ARCH_X86
+ return false;
+#else
+ return true;
+#endif
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb)
+{
+ RT_NOREF(pvDst, pvSrc, cb);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb)
+{
+ RT_NOREF(pvDst, pvSrc, cb);
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/mp-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/mp-r0drv-darwin.cpp
new file mode 100644
index 00000000..7e7ad56e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/mp-r0drv-darwin.cpp
@@ -0,0 +1,324 @@
+/* $Id: mp-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mp.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include "r0drv/mp-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static int32_t volatile g_cMaxCpus = -1;
+
+
+static int rtMpDarwinInitMaxCpus(void)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ int32_t cCpus = -1;
+ size_t oldLen = sizeof(cCpus);
+ int rc = sysctlbyname("hw.ncpu", &cCpus, &oldLen, NULL, NULL);
+ if (rc)
+ {
+ printf("IPRT: sysctlbyname(hw.ncpu) failed with rc=%d!\n", rc);
+ cCpus = 64; /* whatever */
+ }
+
+ ASMAtomicWriteS32(&g_cMaxCpus, cCpus);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return cCpus;
+}
+
+
+DECLINLINE(int) rtMpDarwinMaxCpus(void)
+{
+ int cCpus = g_cMaxCpus;
+ if (RT_UNLIKELY(cCpus <= 0))
+ return rtMpDarwinInitMaxCpus();
+ return cCpus;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ return cpu_number();
+}
+
+
+RTDECL(int) RTMpCurSetIndex(void)
+{
+ return cpu_number();
+}
+
+
+RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu)
+{
+ return *pidCpu = cpu_number();
+}
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS ? (int)idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? (RTCPUID)iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return rtMpDarwinMaxCpus() - 1;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS
+ && idCpu < (RTCPUID)rtMpDarwinMaxCpus();
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ return rtMpDarwinMaxCpus();
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ /** @todo darwin R0 MP */
+ return RTMpGetSet(pSet);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ /** @todo darwin R0 MP */
+ return RTMpGetCount();
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ /** @todo darwin R0 MP */
+ return RTMpIsCpuPossible(idCpu);
+}
+
+
+RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
+{
+ /** @todo darwin R0 MP (rainy day) */
+ RT_NOREF(idCpu);
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
+{
+ /** @todo darwin R0 MP (rainy day) */
+ RT_NOREF(idCpu);
+ return 0;
+}
+
+
+RTDECL(bool) RTMpIsCpuWorkPending(void)
+{
+ /** @todo (not used on non-Windows platforms yet). */
+ return false;
+}
+
+
+/**
+ * Wrapper between the native darwin per-cpu callback and PFNRTWORKER
+ * for the RTMpOnAll API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnAllDarwinWrapper(void *pvArg)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ IPRT_DARWIN_SAVE_EFL_AC();
+ pArgs->pfnWorker(cpu_number(), pArgs->pvUser1, pArgs->pvUser2);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTMPARGS Args;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = NIL_RTCPUID;
+ Args.cHits = 0;
+ mp_rendezvous_no_intrs(rtmpOnAllDarwinWrapper, &Args);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native darwin per-cpu callback and PFNRTWORKER
+ * for the RTMpOnOthers API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnOthersDarwinWrapper(void *pvArg)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ RTCPUID idCpu = cpu_number();
+ if (pArgs->idCpu != idCpu)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+}
+
+
+RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTMPARGS Args;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = RTMpCpuId();
+ Args.cHits = 0;
+ mp_rendezvous_no_intrs(rtmpOnOthersDarwinWrapper, &Args);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native darwin per-cpu callback and PFNRTWORKER
+ * for the RTMpOnSpecific API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnSpecificDarwinWrapper(void *pvArg)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ RTCPUID idCpu = cpu_number();
+ if (pArgs->idCpu == idCpu)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+}
+
+
+RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTMPARGS Args;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu;
+ Args.cHits = 0;
+ mp_rendezvous_no_intrs(rtmpOnSpecificDarwinWrapper, &Args);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return Args.cHits == 1
+ ? VINF_SUCCESS
+ : VERR_CPU_NOT_FOUND;
+}
+
+
+RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
+{
+ RT_ASSERT_INTS_ON();
+
+ if (g_pfnR0DarwinCpuInterrupt == NULL)
+ return VERR_NOT_SUPPORTED;
+ IPRT_DARWIN_SAVE_EFL_AC(); /* paranoia */
+ /// @todo use mp_cpus_kick() when available (since 10.10)? It's probably slower (locks, mask iteration, checks), though...
+ g_pfnR0DarwinCpuInterrupt(idCpu);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
+{
+ return true;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/process-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/process-r0drv-darwin.cpp
new file mode 100644
index 00000000..148e4e08
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/process-r0drv-darwin.cpp
@@ -0,0 +1,56 @@
+/* $Id: process-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Process, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/process.h>
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ return proc_selfpid();
+}
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ return (RTR0PROCESS)current_task();
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/rtStrFormatKernelAddress-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/rtStrFormatKernelAddress-r0drv-darwin.cpp
new file mode 100644
index 00000000..fbabe03e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/rtStrFormatKernelAddress-r0drv-darwin.cpp
@@ -0,0 +1,60 @@
+/* $Id: rtStrFormatKernelAddress-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - IPRT String Formatter, ring-0 addresses.
+ */
+
+/*
+ * 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 RTLOGGROUP_STRING
+#include "the-darwin-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include "internal/string.h"
+
+
+DECLHIDDEN(size_t) rtStrFormatKernelAddress(char *pszBuf, size_t cbBuf, RTR0INTPTR uPtr, signed int cchWidth,
+ signed int cchPrecision, unsigned int fFlags)
+{
+ /*
+ * Kernel addresses don't need obfuscation in R0 because the kernel log is only accessible
+ * as root.
+ */
+ Assert(cbBuf >= 64); RT_NOREF(cbBuf);
+ return RTStrFormatNumber(pszBuf, uPtr, 16, cchWidth, cchPrecision, fFlags);
+}
diff --git a/src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp
new file mode 100644
index 00000000..8e542cec
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp
@@ -0,0 +1,445 @@
+/* $Id: semevent-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Single Release Event Semaphores, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 RTSEMEVENT_WITHOUT_REMAPPING
+#include "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/err.h>
+#include <iprt/list.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Waiter entry. Lives on the stack.
+ */
+typedef struct RTSEMEVENTDARWINENTRY
+{
+ /** The list node. */
+ RTLISTNODE Node;
+ /** Flag set when waking up the thread by signal or destroy. */
+ bool volatile fWokenUp;
+} RTSEMEVENTDARWINENTRY;
+/** Pointer to waiter entry. */
+typedef RTSEMEVENTDARWINENTRY *PRTSEMEVENTDARWINENTRY;
+
+
+/**
+ * Darwin event semaphore.
+ */
+typedef struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Set if there are blocked threads. */
+ bool volatile fHaveBlockedThreads;
+ /** Set if the event object is signaled. */
+ bool volatile fSignaled;
+ /** List of waiting and woken up threads. */
+ RTLISTANCHOR WaitList;
+ /** The spinlock protecting us. */
+ lck_spin_t *pSpinlock;
+} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ RT_NOREF(hClass, pszNameFmt);
+ AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+ AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->fHaveBlockedThreads = false;
+ pThis->fSignaled = false;
+ RTListInit(&pThis->WaitList);
+ Assert(g_pDarwinLockGroup);
+ pThis->pSpinlock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
+ if (pThis->pSpinlock)
+ {
+ *phEventSem = pThis;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ pThis->u32Magic = 0;
+ RTMemFree(pThis);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventDarwinRetain(PRTSEMEVENTINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000); RT_NOREF_PV(cRefs);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventDarwinRelease(PRTSEMEVENTINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ {
+ Assert(pThis->u32Magic != RTSEMEVENT_MAGIC);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
+ RTMemFree(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+}
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ PRTSEMEVENTINTERNAL pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
+ lck_spin_lock(pThis->pSpinlock);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC); /* make the handle invalid */
+ ASMAtomicWriteBool(&pThis->fSignaled, false);
+
+ /* abort waiting threads. */
+ PRTSEMEVENTDARWINENTRY pWaiter;
+ RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTDARWINENTRY, Node)
+ {
+ pWaiter->fWokenUp = true;
+ thread_wakeup_prim((event_t)pWaiter, FALSE /* all threads */, THREAD_RESTART);
+ }
+
+ lck_spin_unlock(pThis->pSpinlock);
+ ASMSetFlags(fIntSaved);
+ rtR0SemEventDarwinRelease(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+
+ /*
+ * Coming here with interrupts disabled should be okay. The thread_wakeup_prim KPI is used
+ * by the interrupt handler IOFilterInterruptEventSource::disableInterruptOccurred() via
+ * signalWorkAvailable(). The only problem is if we have to destroy the event structure,
+ * as RTMemFree does not work with interrupts disabled (IOFree/kfree takes zone mutex).
+ */
+ //RT_ASSERT_INTS_ON(); - we may be called from interrupt context, which seems to be perfectly fine.
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
+ rtR0SemEventDarwinRetain(pThis);
+ lck_spin_lock(pThis->pSpinlock);
+
+ /*
+ * Wake up one thread.
+ */
+ ASMAtomicWriteBool(&pThis->fSignaled, true);
+
+ PRTSEMEVENTDARWINENTRY pWaiter;
+ RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTDARWINENTRY, Node)
+ {
+ if (!pWaiter->fWokenUp)
+ {
+ pWaiter->fWokenUp = true;
+ thread_wakeup_prim((event_t)pWaiter, FALSE /* all threads */, THREAD_AWAKENED);
+ ASMAtomicWriteBool(&pThis->fSignaled, false);
+ break;
+ }
+ }
+
+ lck_spin_unlock(pThis->pSpinlock);
+ ASMSetFlags(fIntSaved);
+ rtR0SemEventDarwinRelease(pThis);
+
+ RT_ASSERT_PREEMPT_CPUID();
+ AssertMsg((fSavedEfl & X86_EFL_IF) == (ASMGetFlags() & X86_EFL_IF), ("fSavedEfl=%#x cur=%#x\n",(uint32_t)fSavedEfl, ASMGetFlags()));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventDarwinWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF(pSrcPos);
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
+ rtR0SemEventDarwinRetain(pThis);
+ lck_spin_lock(pThis->pSpinlock);
+
+ /*
+ * In the signaled state?
+ */
+ int rc;
+ if (ASMAtomicCmpXchgBool(&pThis->fSignaled, false, true))
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait. So, we'll need to convert the timeout and figure
+ * out if it's indefinite or not.
+ */
+ uint64_t uNsAbsTimeout = 1;
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
+ ? uTimeout * UINT32_C(1000000)
+ : UINT64_MAX;
+ if (uTimeout == UINT64_MAX)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ {
+ uint64_t u64Now;
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ {
+ if (uTimeout != 0)
+ {
+ u64Now = RTTimeSystemNanoTS();
+ uNsAbsTimeout = u64Now + uTimeout;
+ if (uNsAbsTimeout < u64Now) /* overflow */
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ }
+ }
+ else
+ {
+ uNsAbsTimeout = uTimeout;
+ u64Now = RTTimeSystemNanoTS();
+ uTimeout = u64Now < uTimeout ? uTimeout - u64Now : 0;
+ }
+ }
+ }
+
+ if ( !(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ && uTimeout == 0)
+ {
+ /*
+ * Poll call, we already checked the condition above so no need to
+ * wait for anything.
+ */
+ rc = VERR_TIMEOUT;
+ }
+ else
+ {
+ RTSEMEVENTDARWINENTRY Waiter;
+ Waiter.fWokenUp = false;
+ RTListAppend(&pThis->WaitList, &Waiter.Node);
+
+ for (;;)
+ {
+ /*
+ * Do the actual waiting.
+ */
+ ASMAtomicWriteBool(&pThis->fHaveBlockedThreads, true);
+ wait_interrupt_t fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE ? THREAD_ABORTSAFE : THREAD_UNINT;
+ wait_result_t rcWait;
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ rcWait = lck_spin_sleep(pThis->pSpinlock, LCK_SLEEP_DEFAULT, (event_t)&Waiter, fInterruptible);
+ else
+ {
+ uint64_t u64AbsTime;
+ nanoseconds_to_absolutetime(uNsAbsTimeout, &u64AbsTime);
+ rcWait = lck_spin_sleep_deadline(pThis->pSpinlock, LCK_SLEEP_DEFAULT,
+ (event_t)&Waiter, fInterruptible, u64AbsTime);
+ }
+
+ /*
+ * Deal with the wait result.
+ */
+ if (RT_LIKELY(pThis->u32Magic == RTSEMEVENT_MAGIC))
+ {
+ switch (rcWait)
+ {
+ case THREAD_AWAKENED:
+ if (RT_LIKELY(Waiter.fWokenUp))
+ rc = VINF_SUCCESS;
+ else if (fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE)
+ rc = VERR_INTERRUPTED;
+ else
+ continue; /* Seen this happen after fork/exec/something. */
+ break;
+
+ case THREAD_TIMED_OUT:
+ Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
+ rc = !Waiter.fWokenUp ? VERR_TIMEOUT : VINF_SUCCESS;
+ break;
+
+ case THREAD_INTERRUPTED:
+ Assert(fInterruptible != THREAD_UNINT);
+ rc = !Waiter.fWokenUp ? VERR_INTERRUPTED : VINF_SUCCESS;
+ break;
+
+ case THREAD_RESTART:
+ AssertMsg(pThis->u32Magic == ~RTSEMEVENT_MAGIC, ("%#x\n", pThis->u32Magic));
+ rc = VERR_SEM_DESTROYED;
+ break;
+
+ default:
+ AssertMsgFailed(("rcWait=%d\n", rcWait));
+ rc = VERR_INTERNAL_ERROR_3;
+ break;
+ }
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+ break;
+ }
+
+ RTListNodeRemove(&Waiter.Node);
+ }
+ }
+
+ lck_spin_unlock(pThis->pSpinlock);
+ ASMSetFlags(fIntSaved);
+ rtR0SemEventDarwinRelease(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventDarwinWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ uint64_t cNs;
+ absolutetime_to_nanoseconds(1, &cNs);
+ return (uint32_t)cNs ? (uint32_t)cNs : 0;
+}
+
+
+RTR0DECL(bool) RTSemEventIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTSemEventIsSignalSafe);
+
diff --git a/src/VBox/Runtime/r0drv/darwin/semeventmulti-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/semeventmulti-r0drv-darwin.cpp
new file mode 100644
index 00000000..4ca53cbf
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/semeventmulti-r0drv-darwin.cpp
@@ -0,0 +1,467 @@
+/* $Id: semeventmulti-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#define RTMEM_NO_WRAP_TO_EF_APIS /* rtR0MemObjNativeProtect depends on this code, so no electrical fences here or we'll \#DF. */
+#include "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name fStateAndGen values
+ * @{ */
+/** The state bit number. */
+#define RTSEMEVENTMULTIDARWIN_STATE_BIT 0
+/** The state mask. */
+#define RTSEMEVENTMULTIDARWIN_STATE_MASK RT_BIT_32(RTSEMEVENTMULTIDARWIN_STATE_BIT)
+/** The generation mask. */
+#define RTSEMEVENTMULTIDARWIN_GEN_MASK ~RTSEMEVENTMULTIDARWIN_STATE_MASK
+/** The generation shift. */
+#define RTSEMEVENTMULTIDARWIN_GEN_SHIFT 1
+/** The initial variable value. */
+#define RTSEMEVENTMULTIDARWIN_STATE_GEN_INIT UINT32_C(0xfffffffc)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Darwin multiple release event semaphore.
+ */
+typedef struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The object state bit and generation counter.
+ * The generation counter is incremented every time the object is
+ * signalled. */
+ uint32_t volatile fStateAndGen;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Set if there are blocked threads. */
+ bool volatile fHaveBlockedThreads;
+ /** The spinlock protecting us. */
+ lck_spin_t *pSpinlock;
+} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ RT_NOREF(hClass, pszNameFmt);
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ AssertCompile(sizeof(RTSEMEVENTMULTIINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phEventMultiSem, VERR_INVALID_POINTER);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->fStateAndGen = RTSEMEVENTMULTIDARWIN_STATE_GEN_INIT;
+ pThis->cRefs = 1;
+ pThis->fHaveBlockedThreads = false;
+ Assert(g_pDarwinLockGroup);
+ pThis->pSpinlock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
+ if (pThis->pSpinlock)
+ {
+ *phEventMultiSem = pThis;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ pThis->u32Magic = 0;
+ RTMemFree(pThis);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiDarwinRetain(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+ RT_NOREF_PV(cRefs);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiDarwinRelease(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ Assert(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC);
+
+ lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
+ RTMemFree(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs > 0);
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
+ lck_spin_lock(pThis->pSpinlock);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC); /* make the handle invalid */
+ ASMAtomicAndU32(&pThis->fStateAndGen, RTSEMEVENTMULTIDARWIN_GEN_MASK);
+ if (pThis->fHaveBlockedThreads)
+ {
+ /* abort waiting threads. */
+ thread_wakeup_prim((event_t)pThis, FALSE /* all threads */, THREAD_RESTART);
+ }
+
+ lck_spin_unlock(pThis->pSpinlock);
+ ASMSetFlags(fIntSaved);
+ rtR0SemEventMultiDarwinRelease(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+
+ /*
+ * Coming here with interrupts disabled should be okay. The thread_wakeup_prim KPI is used
+ * by the interrupt handler IOFilterInterruptEventSource::disableInterruptOccurred() via
+ * signalWorkAvailable(). The only problem is if we have to destroy the event structure,
+ * as RTMemFree does not work with interrupts disabled (IOFree/kfree takes zone mutex).
+ */
+ //RT_ASSERT_INTS_ON(); - we may be called from interrupt context, which seems to be perfectly fine if we disable interrupts.
+
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
+ rtR0SemEventMultiDarwinRetain(pThis);
+ lck_spin_lock(pThis->pSpinlock);
+
+ /*
+ * Set the signal and increment the generation counter.
+ */
+ uint32_t fNew = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ fNew += 1 << RTSEMEVENTMULTIDARWIN_GEN_SHIFT;
+ fNew |= RTSEMEVENTMULTIDARWIN_STATE_MASK;
+ ASMAtomicWriteU32(&pThis->fStateAndGen, fNew);
+
+ /*
+ * Wake up all sleeping threads.
+ */
+ if (pThis->fHaveBlockedThreads)
+ {
+ ASMAtomicWriteBool(&pThis->fHaveBlockedThreads, false);
+ thread_wakeup_prim((event_t)pThis, FALSE /* all threads */, THREAD_AWAKENED);
+ }
+
+ lck_spin_unlock(pThis->pSpinlock);
+ ASMSetFlags(fIntSaved);
+ rtR0SemEventMultiDarwinRelease(pThis);
+
+ RT_ASSERT_PREEMPT_CPUID();
+ AssertMsg((fSavedEfl & X86_EFL_IF) == (ASMGetFlags() & X86_EFL_IF), ("fSavedEfl=%#x cur=%#x\n",(uint32_t)fSavedEfl, ASMGetFlags()));
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
+ rtR0SemEventMultiDarwinRetain(pThis);
+ lck_spin_lock(pThis->pSpinlock);
+
+ ASMAtomicAndU32(&pThis->fStateAndGen, ~RTSEMEVENTMULTIDARWIN_STATE_MASK);
+
+ lck_spin_unlock(pThis->pSpinlock);
+ ASMSetFlags(fIntSaved);
+ rtR0SemEventMultiDarwinRelease(pThis);
+
+ RT_ASSERT_PREEMPT_CPUID();
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param uTimeout See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventMultiDarwinWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF(pSrcPos);
+
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ if (uTimeout != 0 || (fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ RTCCUINTREG const fIntSaved = ASMIntDisableFlags();
+ rtR0SemEventMultiDarwinRetain(pThis);
+ lck_spin_lock(pThis->pSpinlock);
+
+ /*
+ * Is the event already signalled or do we have to wait?
+ */
+ int rc;
+ uint32_t const fOrgStateAndGen = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ if (fOrgStateAndGen & RTSEMEVENTMULTIDARWIN_STATE_MASK)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait. So, we'll need to convert the timeout and figure
+ * out if it's indefinite or not.
+ */
+ uint64_t uNsAbsTimeout = 1;
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
+ ? uTimeout * UINT32_C(1000000)
+ : UINT64_MAX;
+ if (uTimeout == UINT64_MAX)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ {
+ uint64_t u64Now;
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ {
+ if (uTimeout != 0)
+ {
+ u64Now = RTTimeSystemNanoTS();
+ uNsAbsTimeout = u64Now + uTimeout;
+ if (uNsAbsTimeout < u64Now) /* overflow */
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ }
+ }
+ else
+ {
+ uNsAbsTimeout = uTimeout;
+ u64Now = RTTimeSystemNanoTS();
+ uTimeout = u64Now < uTimeout ? uTimeout - u64Now : 0;
+ }
+ }
+ }
+
+ if ( !(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ && uTimeout == 0)
+ {
+ /*
+ * Poll call, we already checked the condition above so no need to
+ * wait for anything.
+ */
+ rc = VERR_TIMEOUT;
+ }
+ else
+ {
+ for (;;)
+ {
+ /*
+ * Do the actual waiting.
+ */
+ ASMAtomicWriteBool(&pThis->fHaveBlockedThreads, true);
+ wait_interrupt_t fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE ? THREAD_ABORTSAFE : THREAD_UNINT;
+ wait_result_t rcWait;
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ rcWait = lck_spin_sleep(pThis->pSpinlock, LCK_SLEEP_DEFAULT, (event_t)pThis, fInterruptible);
+ else
+ {
+ uint64_t u64AbsTime;
+ nanoseconds_to_absolutetime(uNsAbsTimeout, &u64AbsTime);
+ rcWait = lck_spin_sleep_deadline(pThis->pSpinlock, LCK_SLEEP_DEFAULT,
+ (event_t)pThis, fInterruptible, u64AbsTime);
+ }
+
+ /*
+ * Deal with the wait result.
+ */
+ if (RT_LIKELY(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC))
+ {
+ switch (rcWait)
+ {
+ case THREAD_AWAKENED:
+ if (RT_LIKELY(ASMAtomicUoReadU32(&pThis->fStateAndGen) != fOrgStateAndGen))
+ rc = VINF_SUCCESS;
+ else if (fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE)
+ rc = VERR_INTERRUPTED;
+ else
+ continue; /* Seen this happen after fork/exec/something. */
+ break;
+
+ case THREAD_TIMED_OUT:
+ Assert(!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE));
+ rc = VERR_TIMEOUT;
+ break;
+
+ case THREAD_INTERRUPTED:
+ Assert(fInterruptible != THREAD_UNINT);
+ rc = VERR_INTERRUPTED;
+ break;
+
+ case THREAD_RESTART:
+ AssertMsg(pThis->u32Magic == ~RTSEMEVENTMULTI_MAGIC, ("%#x\n", pThis->u32Magic));
+ rc = VERR_SEM_DESTROYED;
+ break;
+
+ default:
+ AssertMsgFailed(("rcWait=%d\n", rcWait));
+ rc = VERR_INTERNAL_ERROR_3;
+ break;
+ }
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+ break;
+ }
+ }
+ }
+
+ lck_spin_unlock(pThis->pSpinlock);
+ ASMSetFlags(fIntSaved);
+ rtR0SemEventMultiDarwinRelease(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return rc;
+}
+
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventMultiDarwinWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventMultiDarwinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventMultiDarwinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventMultiGetResolution(void)
+{
+ uint64_t cNs;
+ absolutetime_to_nanoseconds(1, &cNs);
+ return (uint32_t)cNs ? (uint32_t)cNs : 0;
+}
+
+
+RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/semfastmutex-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/semfastmutex-r0drv-darwin.cpp
new file mode 100644
index 00000000..2d047076
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/semfastmutex-r0drv-darwin.cpp
@@ -0,0 +1,150 @@
+/* $Id: semfastmutex-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the darwin semaphore structure.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The mutex. */
+ lck_mtx_t *pMtx;
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ AssertCompile(sizeof(RTSEMFASTMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phFastMtx, VERR_INVALID_POINTER);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ PRTSEMFASTMUTEXINTERNAL pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ Assert(g_pDarwinLockGroup);
+ pThis->pMtx = lck_mtx_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
+ if (pThis->pMtx)
+ {
+ *phFastMtx = pThis;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ Assert(g_pDarwinLockGroup);
+ lck_mtx_free(pThis->pMtx, g_pDarwinLockGroup);
+ pThis->pMtx = NULL;
+ RTMemFree(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ lck_mtx_lock(pThis->pMtx);
+
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ lck_mtx_unlock(pThis->pMtx);
+
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC();
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/semmutex-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/semmutex-r0drv-darwin.cpp
new file mode 100644
index 00000000..ae5f7f74
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/semmutex-r0drv-darwin.cpp
@@ -0,0 +1,417 @@
+/* $Id: semmutex-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Mutex Semaphores, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 RTSEMMUTEX_WITHOUT_REMAPPING
+#include "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Darwin mutex semaphore.
+ */
+typedef struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The number of waiting threads. */
+ uint32_t cWaiters;
+ /** The number of references. */
+ uint32_t volatile cRefs;
+ /** The number of recursions. */
+ uint32_t cRecursions;
+ /** The handle of the owner thread. */
+ RTNATIVETHREAD hNativeOwner;
+ /** The spinlock protecting us. */
+ lck_spin_t *pSpinlock;
+} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL;
+
+
+
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ RT_NOREF(hClass, uSubClass, pszNameFmt);
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ AssertCompile(sizeof(RTSEMMUTEXINTERNAL) > sizeof(void *));
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ pThis->cWaiters = 0;
+ pThis->cRefs = 1;
+ pThis->cRecursions = 0;
+ pThis->hNativeOwner = NIL_RTNATIVETHREAD;
+ Assert(g_pDarwinLockGroup);
+ pThis->pSpinlock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
+ if (pThis->pSpinlock)
+ {
+ *phMutexSem = pThis;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Called when the refcount reaches zero.
+ */
+static void rtSemMutexDarwinFree(PRTSEMMUTEXINTERNAL pThis)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ lck_spin_unlock(pThis->pSpinlock);
+ lck_spin_destroy(pThis->pSpinlock, g_pDarwinLockGroup);
+ RTMemFree(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem;
+ if (pThis == NIL_RTSEMMUTEX)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Kill it, wake up all waiting threads and release the reference.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMMUTEX_MAGIC, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+ lck_spin_lock(pThis->pSpinlock);
+
+ if (pThis->cWaiters > 0)
+ thread_wakeup_prim((event_t)pThis, FALSE /* one_thread */, THREAD_RESTART);
+
+ if (ASMAtomicDecU32(&pThis->cRefs) == 0)
+ rtSemMutexDarwinFree(pThis);
+ else
+ lck_spin_unlock(pThis->pSpinlock);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal worker for the sleep scenario.
+ *
+ * Called owning the spinlock, returns without it.
+ *
+ * @returns IPRT status code.
+ * @param pThis The mutex instance.
+ * @param cMillies The timeout.
+ * @param fInterruptible Whether it's interruptible
+ * (RTSemMutexRequestNoResume) or not
+ * (RTSemMutexRequest).
+ * @param hNativeSelf The thread handle of the caller.
+ */
+static int rtR0SemMutexDarwinRequestSleep(PRTSEMMUTEXINTERNAL pThis, RTMSINTERVAL cMillies,
+ wait_interrupt_t fInterruptible, RTNATIVETHREAD hNativeSelf)
+{
+ /*
+ * Grab a reference and indicate that we're waiting.
+ */
+ pThis->cWaiters++;
+ ASMAtomicIncU32(&pThis->cRefs);
+
+ /*
+ * Go to sleep, use the address of the mutex instance as sleep/blocking/event id.
+ */
+ wait_result_t rcWait;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rcWait = lck_spin_sleep(pThis->pSpinlock, LCK_SLEEP_DEFAULT, (event_t)pThis, fInterruptible);
+ else
+ {
+ uint64_t u64AbsTime;
+ nanoseconds_to_absolutetime(cMillies * UINT64_C(1000000), &u64AbsTime);
+ u64AbsTime += mach_absolute_time();
+
+ rcWait = lck_spin_sleep_deadline(pThis->pSpinlock, LCK_SLEEP_DEFAULT,
+ (event_t)pThis, fInterruptible, u64AbsTime);
+ }
+
+ /*
+ * Translate the rc.
+ */
+ int rc;
+ switch (rcWait)
+ {
+ case THREAD_AWAKENED:
+ if (RT_LIKELY(pThis->u32Magic == RTSEMMUTEX_MAGIC))
+ {
+ if (RT_LIKELY( pThis->cRecursions == 0
+ && pThis->hNativeOwner == NIL_RTNATIVETHREAD))
+ {
+ pThis->cRecursions = 1;
+ pThis->hNativeOwner = hNativeSelf;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Assert(pThis->cRecursions == 0);
+ Assert(pThis->hNativeOwner == NIL_RTNATIVETHREAD);
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+ break;
+
+ case THREAD_TIMED_OUT:
+ Assert(cMillies != RT_INDEFINITE_WAIT);
+ rc = VERR_TIMEOUT;
+ break;
+
+ case THREAD_INTERRUPTED:
+ Assert(fInterruptible);
+ rc = VERR_INTERRUPTED;
+ break;
+
+ case THREAD_RESTART:
+ Assert(pThis->u32Magic == ~RTSEMMUTEX_MAGIC);
+ rc = VERR_SEM_DESTROYED;
+ break;
+
+ default:
+ AssertMsgFailed(("rcWait=%d\n", rcWait));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+
+ /*
+ * Dereference it and quit the lock.
+ */
+ Assert(pThis->cWaiters > 0);
+ pThis->cWaiters--;
+
+ Assert(pThis->cRefs > 0);
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ rtSemMutexDarwinFree(pThis);
+ else
+ lck_spin_unlock(pThis->pSpinlock);
+ return rc;
+}
+
+
+/**
+ * Internal worker for RTSemMutexRequest and RTSemMutexRequestNoResume
+ *
+ * @returns IPRT status code.
+ * @param hMutexSem The mutex handle.
+ * @param cMillies The timeout.
+ * @param fInterruptible Whether it's interruptible
+ * (RTSemMutexRequestNoResume) or not
+ * (RTSemMutexRequest).
+ */
+DECLINLINE(int) rtR0SemMutexDarwinRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, wait_interrupt_t fInterruptible)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Grab the lock and check out the state.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ int rc = VINF_SUCCESS;
+ lck_spin_lock(pThis->pSpinlock);
+
+ /* Recursive call? */
+ if (pThis->hNativeOwner == hNativeSelf)
+ {
+ Assert(pThis->cRecursions > 0);
+ Assert(pThis->cRecursions < 256);
+ pThis->cRecursions++;
+ }
+
+ /* Is it free and nobody ahead of us in the queue? */
+ else if ( pThis->hNativeOwner == NIL_RTNATIVETHREAD
+ && pThis->cWaiters == 0)
+ {
+ pThis->hNativeOwner = hNativeSelf;
+ pThis->cRecursions = 1;
+ }
+
+ /* Polling call? */
+ else if (cMillies == 0)
+ rc = VERR_TIMEOUT;
+
+ /* Yawn, time for a nap... */
+ else
+ {
+ rc = rtR0SemMutexDarwinRequestSleep(pThis, cMillies, fInterruptible, hNativeSelf);
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC();
+ return rc;
+ }
+
+ lck_spin_unlock(pThis->pSpinlock);
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC();
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtR0SemMutexDarwinRequest(hMutexSem, cMillies, THREAD_UNINT);
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RT_SRC_POS_NOREF(); RT_NOREF(uId);
+ return RTSemMutexRequest(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtR0SemMutexDarwinRequest(hMutexSem, cMillies, THREAD_ABORTSAFE);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RT_SRC_POS_NOREF(); RT_NOREF(uId);
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Take the lock and do the job.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ int rc = VINF_SUCCESS;
+ lck_spin_lock(pThis->pSpinlock);
+
+ if (pThis->hNativeOwner == hNativeSelf)
+ {
+ Assert(pThis->cRecursions > 0);
+ if (--pThis->cRecursions == 0)
+ {
+ pThis->hNativeOwner = NIL_RTNATIVETHREAD;
+ if (pThis->cWaiters > 0)
+ thread_wakeup_prim((event_t)pThis, TRUE /* one_thread */, THREAD_AWAKENED);
+
+ }
+ }
+ else
+ rc = VERR_NOT_OWNER;
+
+ lck_spin_unlock(pThis->pSpinlock);
+
+ AssertRC(rc);
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Take the lock and do the check.
+ */
+ lck_spin_lock(pThis->pSpinlock);
+ bool fRc = pThis->hNativeOwner != NIL_RTNATIVETHREAD;
+ lck_spin_unlock(pThis->pSpinlock);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return fRc;
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/spinlock-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/spinlock-r0drv-darwin.cpp
new file mode 100644
index 00000000..13b56bca
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/spinlock-r0drv-darwin.cpp
@@ -0,0 +1,187 @@
+/* $Id: spinlock-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/spinlock.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the KSPIN_LOCK type.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Saved interrupt flag. */
+ uint32_t volatile fIntSaved;
+ /** Creation flags. */
+ uint32_t fFlags;
+ /** The Darwin spinlock structure. */
+ lck_spin_t *pSpinLock;
+ /** The spinlock name. */
+ const char *pszName;
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ /*
+ * Allocate.
+ */
+ AssertCompile(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ /*
+ * Initialize & return.
+ */
+ pThis->u32Magic = RTSPINLOCK_MAGIC;
+ pThis->fIntSaved = 0;
+ pThis->fFlags = fFlags;
+ pThis->pszName = pszName;
+ Assert(g_pDarwinLockGroup);
+ pThis->pSpinLock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
+ if (pThis->pSpinLock)
+ {
+ *pSpinlock = pThis;
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertMsgReturn(pThis->u32Magic == RTSPINLOCK_MAGIC,
+ ("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Make the lock invalid and release the memory.
+ */
+ ASMAtomicIncU32(&pThis->u32Magic);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ Assert(g_pDarwinLockGroup);
+ lck_spin_free(pThis->pSpinLock, g_pDarwinLockGroup);
+ pThis->pSpinLock = NULL;
+
+ RTMemFree(pThis);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+ uint32_t fIntSaved = ASMGetFlags();
+ ASMIntDisable();
+ lck_spin_lock(pThis->pSpinLock);
+ pThis->fIntSaved = fIntSaved;
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC_EX(fIntSaved);
+ }
+ else
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ lck_spin_lock(pThis->pSpinLock);
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC();
+ }
+}
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+ uint32_t fIntSaved = pThis->fIntSaved;
+ pThis->fIntSaved = 0;
+ lck_spin_unlock(pThis->pSpinLock);
+ ASMSetFlags(fIntSaved);
+ }
+ else
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ lck_spin_unlock(pThis->pSpinLock);
+ IPRT_DARWIN_RESTORE_EFL_ONLY_AC();
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/the-darwin-kernel.h b/src/VBox/Runtime/r0drv/darwin/the-darwin-kernel.h
new file mode 100644
index 00000000..03c94a74
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/the-darwin-kernel.h
@@ -0,0 +1,262 @@
+/* $Id: the-darwin-kernel.h $ */
+/** @file
+ * IPRT - Include all necessary headers for the Darwing kernel.
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_darwin_the_darwin_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_darwin_the_darwin_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Problematic header(s) containing conflicts with IPRT first. (FreeBSD has fixed these ages ago.) */
+#define __STDC_CONSTANT_MACROS
+#define __STDC_LIMIT_MACROS
+#include <sys/param.h>
+#include <mach/vm_param.h>
+#undef ALIGN
+#undef MIN
+#undef MAX
+#undef PAGE_SIZE
+#undef PAGE_SHIFT
+#undef PVM
+
+
+/* Include the IPRT definitions of the conflicting #defines & typedefs. */
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/param.h>
+
+
+/* After including cdefs, we can check that this really is Darwin. */
+#ifndef RT_OS_DARWIN
+# error "RT_OS_DARWIN must be defined!"
+#endif
+
+#if defined(__clang__) || RT_GNUC_PREREQ(4, 4)
+# pragma GCC diagnostic push
+#endif
+#if defined(__clang__) || RT_GNUC_PREREQ(4, 2)
+# pragma GCC diagnostic ignored "-Wc++11-extensions"
+# pragma GCC diagnostic ignored "-Wc99-extensions"
+# pragma GCC diagnostic ignored "-Wextra-semi"
+# pragma GCC diagnostic ignored "-Wzero-length-array"
+# pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#endif
+
+/* now we're ready for including the rest of the Darwin headers. */
+#include <kern/thread.h>
+#include <kern/clock.h>
+#include <kern/sched_prim.h>
+#include <kern/locks.h>
+#if defined(RT_ARCH_X86) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+# include <i386/mp_events.h>
+#endif
+#include <libkern/libkern.h>
+#include <libkern/sysctl.h>
+#include <libkern/version.h>
+#include <mach/thread_act.h>
+#include <mach/vm_map.h>
+#include <mach/vm_region.h>
+#include <pexpert/pexpert.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/fcntl.h>
+#include <IOKit/IOTypes.h>
+#include <IOKit/IOLib.h> /* Note! Has Assert down as a function. */
+#define _OS_OSUNSERIALIZE_H /* HACK ALERT! Block importing OSUnserialized.h as it causes compilation trouble with
+ newer clang versions and the 10.15 SDK, and we really don't need it. Sample error:
+ libkern/c++/OSUnserialize.h:72:2: error: use of OSPtr outside of a return type [-Werror,-Wossharedptr-misuse] */
+#include <IOKit/IOMemoryDescriptor.h>
+#include <IOKit/IOBufferMemoryDescriptor.h>
+#include <IOKit/IOMapper.h>
+
+#if defined(__clang__) || RT_GNUC_PREREQ(4, 4)
+# pragma GCC diagnostic pop
+#endif
+
+
+/* See osfmk/kern/ast.h. */
+#ifndef AST_PREEMPT
+# define AST_PREEMPT UINT32_C(1)
+# define AST_QUANTUM UINT32_C(2)
+# define AST_URGENT UINT32_C(4)
+#endif
+
+/* This flag was added in 10.6, it seems. Should be harmless in earlier
+ releases... */
+#if __MAC_OS_X_VERSION_MAX_ALLOWED < 1060
+# define kIOMemoryMapperNone UINT32_C(0x800)
+#endif
+
+/* This flag was added in 10.8.2, it seems. */
+#if __MAC_OS_X_VERSION_MAX_ALLOWED < 1082
+# define kIOMemoryHostPhysicallyContiguous UINT32_C(0x00000080)
+#endif
+
+/** @name Macros for preserving EFLAGS.AC (despair / paranoid)
+ * @remarks Unlike linux, we have to restore it unconditionally on darwin.
+ * @{ */
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/x86.h>
+#define IPRT_DARWIN_SAVE_EFL_AC() RTCCUINTREG const fSavedEfl = ASMGetFlags();
+#define IPRT_DARWIN_RESTORE_EFL_AC() ASMSetFlags(fSavedEfl)
+#define IPRT_DARWIN_RESTORE_EFL_ONLY_AC() ASMChangeFlags(~X86_EFL_AC, fSavedEfl & X86_EFL_AC)
+#define IPRT_DARWIN_RESTORE_EFL_ONLY_AC_EX(a_fSavedEfl) ASMChangeFlags(~X86_EFL_AC, (a_fSavedEfl) & X86_EFL_AC)
+/** @} */
+
+
+RT_C_DECLS_BEGIN
+
+/* mach/vm_types.h */
+typedef struct pmap *pmap_t;
+
+/* vm/vm_kern.h */
+extern vm_map_t kernel_map;
+
+/* vm/pmap.h */
+extern pmap_t kernel_pmap;
+
+/* kern/task.h */
+extern vm_map_t get_task_map(task_t);
+
+/* osfmk/i386/pmap.h */
+extern ppnum_t pmap_find_phys(pmap_t, addr64_t);
+
+/* vm/vm_map.h */
+extern kern_return_t vm_map_wire(vm_map_t, vm_map_offset_t, vm_map_offset_t, vm_prot_t, boolean_t);
+extern kern_return_t vm_map_unwire(vm_map_t, vm_map_offset_t, vm_map_offset_t, boolean_t);
+
+/* mach/i386/thread_act.h */
+extern kern_return_t thread_terminate(thread_t);
+
+/* osfmk/i386/mp.h */
+extern void mp_rendezvous(void (*)(void *), void (*)(void *), void (*)(void *), void *);
+extern void mp_rendezvous_no_intrs(void (*)(void *), void *);
+
+/* osfmk/i386/cpu_data.h */
+struct my_cpu_data_x86
+{
+ struct my_cpu_data_x86 *cpu_this;
+ thread_t cpu_active_thread;
+ void *cpu_int_state;
+ vm_offset_t cpu_active_stack;
+ vm_offset_t cpu_kernel_stack;
+ vm_offset_t cpu_int_stack_top;
+ int cpu_preemption_level;
+ int cpu_simple_lock_count;
+ int cpu_interrupt_level;
+ int cpu_number;
+ int cpu_phys_number;
+ cpu_id_t cpu_id;
+ int cpu_signals;
+ int cpu_mcount_off;
+ /*ast_t*/uint32_t cpu_pending_ast;
+ int cpu_type;
+ int cpu_subtype;
+ int cpu_threadtype;
+ int cpu_running;
+};
+
+/* osfmk/i386/cpu_number.h */
+extern int cpu_number(void);
+
+/* osfmk/vm/vm_user.c */
+extern kern_return_t vm_protect(vm_map_t, vm_offset_t, vm_size_t, boolean_t, vm_prot_t);
+/*extern kern_return_t vm_region(vm_map_t, vm_address_t *, vm_size_t *, vm_region_flavor_t, vm_region_info_t,
+ mach_msg_type_number_t *, mach_port_t *);*/
+
+/* i386/machine_routines.h */
+extern int ml_get_max_cpus(void);
+
+RT_C_DECLS_END
+
+
+/*
+ * Internals of the Darwin Ring-0 IPRT.
+ */
+RT_C_DECLS_BEGIN
+
+/* initterm-r0drv-darwin.cpp. */
+typedef uint32_t * (*PFNR0DARWINASTPENDING)(void);
+typedef void (*PFNR0DARWINCPUINTERRUPT)(int);
+extern DECL_HIDDEN_DATA(lck_grp_t *) g_pDarwinLockGroup;
+extern DECL_HIDDEN_DATA(PFNR0DARWINASTPENDING) g_pfnR0DarwinAstPending;
+extern DECL_HIDDEN_DATA(PFNR0DARWINCPUINTERRUPT) g_pfnR0DarwinCpuInterrupt;
+#ifdef DEBUG /* Used once for debugging memory issues (see #9466). */
+typedef kern_return_t (*PFNR0DARWINVMFAULTEXTERNAL)(vm_map_t, vm_map_offset_t, vm_prot_t, boolean_t, int, pmap_t, vm_map_offset_t);
+extern DECL_HIDDEN_DATA(PFNR0DARWINVMFAULTEXTERNAL) g_pfnR0DarwinVmFaultExternal;
+#endif
+
+/* threadpreempt-r0drv-darwin.cpp */
+int rtThreadPreemptDarwinInit(void);
+void rtThreadPreemptDarwinTerm(void);
+
+RT_C_DECLS_END
+
+
+/**
+ * Converts from nanoseconds to Darwin absolute time units.
+ * @returns Darwin absolute time.
+ * @param u64Nano Time interval in nanoseconds
+ */
+DECLINLINE(uint64_t) rtDarwinAbsTimeFromNano(const uint64_t u64Nano)
+{
+ uint64_t u64AbsTime;
+ nanoseconds_to_absolutetime(u64Nano, &u64AbsTime);
+ return u64AbsTime;
+}
+
+
+#include <iprt/err.h>
+
+/**
+ * Convert from mach kernel return code to IPRT status code.
+ * @todo put this where it belongs! (i.e. in a separate file and prototype in iprt/err.h)
+ */
+DECLINLINE(int) RTErrConvertFromMachKernReturn(kern_return_t rc)
+{
+ switch (rc)
+ {
+ case KERN_SUCCESS: return VINF_SUCCESS;
+ default: return VERR_GENERAL_FAILURE;
+ }
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_darwin_the_darwin_kernel_h */
+
diff --git a/src/VBox/Runtime/r0drv/darwin/thread-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/thread-r0drv-darwin.cpp
new file mode 100644
index 00000000..963546ab
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/thread-r0drv-darwin.cpp
@@ -0,0 +1,92 @@
+/* $Id: thread-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Threads, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)current_thread();
+}
+
+
+static int rtR0ThreadDarwinSleepCommon(RTMSINTERVAL cMillies)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ uint64_t u64Deadline;
+ clock_interval_to_deadline(cMillies, kMillisecondScale, &u64Deadline);
+ clock_delay_until(u64Deadline);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadDarwinSleepCommon(cMillies);
+}
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadDarwinSleepCommon(cMillies);
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ thread_block(THREAD_CONTINUE_NULL);
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return true; /* this is fishy */
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/thread2-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/thread2-r0drv-darwin.cpp
new file mode 100644
index 00000000..67f6afcf
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/thread2-r0drv-darwin.cpp
@@ -0,0 +1,202 @@
+/* $Id: thread2-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/thread.h"
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ /* No TLS in Ring-0. :-/ */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative((RTNATIVETHREAD)current_thread());
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ /*
+ * Convert the priority type to scheduling policies.
+ * (This is really just guess work.)
+ */
+ bool fSetExtended = false;
+ thread_extended_policy Extended = { true };
+ bool fSetTimeContstraint = false;
+ thread_time_constraint_policy TimeConstraint = { 0, 0, 0, true };
+ thread_precedence_policy Precedence = { 0 };
+ switch (enmType)
+ {
+ case RTTHREADTYPE_INFREQUENT_POLLER:
+ Precedence.importance = 1;
+ break;
+
+ case RTTHREADTYPE_EMULATION:
+ Precedence.importance = 30;
+ break;
+
+ case RTTHREADTYPE_DEFAULT:
+ Precedence.importance = 31;
+ break;
+
+ case RTTHREADTYPE_MSG_PUMP:
+ Precedence.importance = 34;
+ break;
+
+ case RTTHREADTYPE_IO:
+ Precedence.importance = 98;
+ break;
+
+ case RTTHREADTYPE_TIMER:
+ Precedence.importance = 0x7fffffff;
+
+ fSetExtended = true;
+ Extended.timeshare = FALSE;
+
+ fSetTimeContstraint = true;
+ TimeConstraint.period = 0; /* not really true for a real timer thread, but we've really no idea. */
+ TimeConstraint.computation = rtDarwinAbsTimeFromNano(100000); /* 100 us*/
+ TimeConstraint.constraint = rtDarwinAbsTimeFromNano(500000); /* 500 us */
+ TimeConstraint.preemptible = FALSE;
+ break;
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+ RT_ASSERT_INTS_ON();
+
+ /*
+ * Do the actual modification.
+ */
+ kern_return_t kr = thread_policy_set((thread_t)pThread->Core.Key, THREAD_PRECEDENCE_POLICY,
+ (thread_policy_t)&Precedence, THREAD_PRECEDENCE_POLICY_COUNT);
+ AssertMsg(kr == KERN_SUCCESS, ("%rc\n", kr)); NOREF(kr);
+
+ if (fSetExtended)
+ {
+ kr = thread_policy_set((thread_t)pThread->Core.Key, THREAD_EXTENDED_POLICY,
+ (thread_policy_t)&Extended, THREAD_EXTENDED_POLICY_COUNT);
+ AssertMsg(kr == KERN_SUCCESS, ("%rc\n", kr));
+ }
+
+ if (fSetTimeContstraint)
+ {
+ kr = thread_policy_set((thread_t)pThread->Core.Key, THREAD_TIME_CONSTRAINT_POLICY,
+ (thread_policy_t)&TimeConstraint, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+ AssertMsg(kr == KERN_SUCCESS, ("%rc\n", kr));
+ }
+
+ return VINF_SUCCESS; /* ignore any errors for now */
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ RT_NOREF(pThread);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ RT_NOREF(pThread);
+ /** @todo fix RTThreadWait/RTR0Term race on darwin. */
+ RTThreadSleep(1);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ RT_NOREF(pThread);
+}
+
+
+/**
+ * Native kernel thread wrapper function.
+ *
+ * This will forward to rtThreadMain and do termination upon return.
+ *
+ * @param pvArg Pointer to the argument package.
+ * @param Ignored Wait result, which we ignore.
+ */
+static void rtThreadNativeMain(void *pvArg, wait_result_t Ignored)
+{
+ RT_NOREF(Ignored);
+ const thread_t Self = current_thread();
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArg;
+
+ rtThreadMain(pThread, (RTNATIVETHREAD)Self, &pThread->szName[0]);
+
+ kern_return_t kr = thread_terminate(Self);
+ AssertFatalMsgFailed(("kr=%d\n", kr));
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ thread_t NativeThread;
+ kern_return_t kr = kernel_thread_start(rtThreadNativeMain, pThreadInt, &NativeThread);
+ if (kr == KERN_SUCCESS)
+ {
+ *pNativeThread = (RTNATIVETHREAD)NativeThread;
+ thread_deallocate(NativeThread);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return RTErrConvertFromMachKernReturn(kr);
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/threadpreempt-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/threadpreempt-r0drv-darwin.cpp
new file mode 100644
index 00000000..1c5df6bf
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/threadpreempt-r0drv-darwin.cpp
@@ -0,0 +1,213 @@
+/* $Id: threadpreempt-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Thread Preemption, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/errcore.h>
+#include <iprt/mp.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTDARWINPREEMPTHACK
+{
+ /** The spinlock we exploit for disabling preemption. */
+ lck_spin_t *pSpinLock;
+ /** The preemption count for this CPU, to guard against nested calls. */
+ uint32_t cRecursion;
+} RTDARWINPREEMPTHACK;
+typedef RTDARWINPREEMPTHACK *PRTDARWINPREEMPTHACK;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static RTDARWINPREEMPTHACK g_aPreemptHacks[RTCPUSET_MAX_CPUS];
+
+
+/**
+ * Allocates the per-cpu spin locks used to disable preemption.
+ *
+ * Called by rtR0InitNative.
+ */
+int rtThreadPreemptDarwinInit(void)
+{
+ Assert(g_pDarwinLockGroup);
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_aPreemptHacks); i++)
+ {
+ g_aPreemptHacks[i].pSpinLock = lck_spin_alloc_init(g_pDarwinLockGroup, LCK_ATTR_NULL);
+ if (!g_aPreemptHacks[i].pSpinLock)
+ return VERR_NO_MEMORY; /* (The caller will invoke rtThreadPreemptDarwinTerm) */
+ }
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Frees the per-cpu spin locks used to disable preemption.
+ *
+ * Called by rtR0TermNative.
+ */
+void rtThreadPreemptDarwinTerm(void)
+{
+ IPRT_DARWIN_SAVE_EFL_AC();
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_aPreemptHacks); i++)
+ if (g_aPreemptHacks[i].pSpinLock)
+ {
+ lck_spin_free(g_aPreemptHacks[i].pSpinLock, g_pDarwinLockGroup);
+ g_aPreemptHacks[i].pSpinLock = NULL;
+ }
+
+ IPRT_DARWIN_RESTORE_EFL_AC();
+}
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ RT_NOREF(hThread);
+ Assert(hThread == NIL_RTTHREAD);
+ return preemption_enabled();
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ RT_NOREF(hThread);
+ if (!g_pfnR0DarwinAstPending)
+ return false;
+ uint32_t volatile *pfAstPending = g_pfnR0DarwinAstPending(); AssertPtr(pfAstPending);
+ uint32_t const fAstPending = *pfAstPending;
+
+ AssertMsg(!(fAstPending & UINT32_C(0xfffe0000)), ("%#x\n", fAstPending));
+ return (fAstPending & (AST_PREEMPT | AST_QUANTUM | AST_URGENT)) != 0;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* yes, we think that RTThreadPreemptIsPending is reliable... */
+ return g_pfnR0DarwinAstPending != NULL;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+ /* yes, kernel preemption is possible. */
+ return true;
+}
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 0);
+ pState->u32Reserved = 42;
+
+ /*
+ * Disable to prevent preemption while we grab the per-cpu spin lock.
+ * Note! Only take the lock on the first call or we end up spinning for ever.
+ */
+ RTCCUINTREG fSavedFlags = ASMIntDisableFlags();
+ RTCPUID idCpu = RTMpCpuId();
+ if (RT_UNLIKELY(idCpu < RT_ELEMENTS(g_aPreemptHacks)))
+ {
+ Assert(g_aPreemptHacks[idCpu].cRecursion < UINT32_MAX / 2);
+ if (++g_aPreemptHacks[idCpu].cRecursion == 1)
+ {
+ lck_spin_t *pSpinLock = g_aPreemptHacks[idCpu].pSpinLock;
+ if (pSpinLock)
+ lck_spin_lock(pSpinLock);
+ else
+ AssertFailed();
+ }
+ }
+ ASMSetFlags(fSavedFlags);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+}
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 42);
+ pState->u32Reserved = 0;
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+
+ RTCPUID idCpu = RTMpCpuId();
+ if (RT_UNLIKELY(idCpu < RT_ELEMENTS(g_aPreemptHacks)))
+ {
+ Assert(g_aPreemptHacks[idCpu].cRecursion > 0);
+ if (--g_aPreemptHacks[idCpu].cRecursion == 0)
+ {
+ lck_spin_t *pSpinLock = g_aPreemptHacks[idCpu].pSpinLock;
+ if (pSpinLock)
+ {
+ IPRT_DARWIN_SAVE_EFL_AC();
+ lck_spin_unlock(pSpinLock);
+ IPRT_DARWIN_RESTORE_EFL_AC();
+ }
+ else
+ AssertFailed();
+ }
+ }
+}
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); NOREF(hThread);
+ /** @todo Darwin: Implement RTThreadIsInInterrupt. Required for guest
+ * additions! */
+ return !ASMIntAreEnabled();
+}
+
diff --git a/src/VBox/Runtime/r0drv/darwin/time-r0drv-darwin.cpp b/src/VBox/Runtime/r0drv/darwin/time-r0drv-darwin.cpp
new file mode 100644
index 00000000..75c8ca8a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/darwin/time-r0drv-darwin.cpp
@@ -0,0 +1,108 @@
+/* $Id: time-r0drv-darwin.cpp $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, Darwin.
+ */
+
+/*
+ * 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 RTLOGGROUP_TIME
+#include "the-darwin-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/time.h>
+
+#include <iprt/asm.h>
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+ static int8_t s_fSimple = -1;
+
+ /* first call: check if life is simple or not. */
+ if (s_fSimple < 0)
+ {
+ struct mach_timebase_info Info;
+ clock_timebase_info(&Info);
+ ASMAtomicXchgS8((int8_t * volatile)&s_fSimple, Info.denom == 1 && Info.numer == 1);
+ }
+
+ /* special case: absolute time is in nanoseconds */
+ if (s_fSimple)
+ return mach_absolute_time();
+
+ /* general case: let mach do the mult/div for us. */
+ uint64_t u64;
+ absolutetime_to_nanoseconds(mach_absolute_time(), &u64);
+ return u64;
+}
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
+ uint32_t uSecs;
+ uint32_t uNanosecs;
+#else
+ clock_sec_t uSecs;
+ clock_nsec_t uNanosecs;
+#endif
+ clock_get_calendar_nanotime(&uSecs, &uNanosecs);
+ return RTTimeSpecSetNano(pTime, (int64_t)uSecs * RT_NS_1SEC + uNanosecs);
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/Makefile.kup b/src/VBox/Runtime/r0drv/freebsd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c
new file mode 100644
index 00000000..6dd27fd3
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c
@@ -0,0 +1,153 @@
+/* $Id: alloc-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/param.h>
+
+#include "r0drv/alloc-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/* These two statements will define two globals and add initializers
+ and destructors that will be called at load/unload time (I think). */
+MALLOC_DEFINE(M_IPRTHEAP, "iprtheap", "IPRT - heap");
+MALLOC_DEFINE(M_IPRTCONT, "iprtcont", "IPRT - contiguous");
+
+
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ size_t cbAllocated = cb;
+ PRTMEMHDR pHdr = (PRTMEMHDR)malloc(cb + sizeof(RTMEMHDR), M_IPRTHEAP,
+ fFlags & RTMEMHDR_FLAG_ZEROED ? M_NOWAIT | M_ZERO : M_NOWAIT);
+ if (RT_LIKELY(pHdr))
+ {
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = cbAllocated;
+ pHdr->cbReq = cb;
+
+ *ppHdr = pHdr;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr)
+{
+ pHdr->u32Magic += 1;
+ free(pHdr, M_IPRTHEAP);
+}
+
+
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ void *pv;
+
+ /*
+ * Validate input.
+ */
+ AssertPtr(pPhys);
+ Assert(cb > 0);
+
+ /*
+ * This API works in pages, so no need to do any size aligning.
+ */
+ pv = contigmalloc(cb, /* size */
+ M_IPRTCONT, /* type */
+ M_NOWAIT | M_ZERO, /* flags */
+ 0, /* lowest physical address*/
+ _4G-1, /* highest physical address */
+ PAGE_SIZE, /* alignment. */
+ 0); /* boundary */
+ if (pv)
+ {
+ Assert(!((uintptr_t)pv & PAGE_OFFSET_MASK));
+ *pPhys = vtophys(pv);
+ Assert(!(*pPhys & PAGE_OFFSET_MASK));
+ }
+ return pv;
+}
+
+
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
+{
+ if (pv)
+ {
+ AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv));
+ contigfree(pv, cb, M_IPRTCONT);
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/assert-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/assert-r0drv-freebsd.c
new file mode 100644
index 00000000..b2179684
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/assert-r0drv-freebsd.c
@@ -0,0 +1,104 @@
+/* $Id: assert-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+
+#include "internal/assert.h"
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ printf("\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%d) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+ char szMsg[256];
+
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+ printf("%s", szMsg);
+
+ NOREF(fInitial);
+}
+
+
+RTR0DECL(void) RTR0AssertPanicSystem(void)
+{
+ /** @todo implement RTR0AssertPanicSystem. */
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c
new file mode 100644
index 00000000..b62a6576
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c
@@ -0,0 +1,87 @@
+/* $Id: initterm-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Initialization & Termination, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+
+#include <iprt/errcore.h>
+
+#include "internal/initterm.h"
+
+
+DECLHIDDEN(int) rtR0InitNative(void)
+{
+ /* nothing to do */
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0TermNative(void)
+{
+ /* nothing to undo */
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c
new file mode 100644
index 00000000..a20c0509
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c
@@ -0,0 +1,906 @@
+/* $Id: memobj-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen, Andriy Gapon.
+ *
+ * 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 code is based on:
+ *
+ * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ * Copyright (c) 2011 Andriy Gapon <avg@FreeBSD.org>
+ *
+ * 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 "the-freebsd-kernel.h"
+
+#include <iprt/memobj.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include "internal/memobj.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The FreeBSD version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJFREEBSD
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ /** The VM object associated with the allocation. */
+ vm_object_t pObject;
+} RTR0MEMOBJFREEBSD, *PRTR0MEMOBJFREEBSD;
+
+
+MALLOC_DEFINE(M_IPRTMOBJ, "iprtmobj", "IPRT - R0MemObj");
+
+
+/**
+ * Gets the virtual memory map the specified object is mapped into.
+ *
+ * @returns VM map handle on success, NULL if no map.
+ * @param pMem The memory object.
+ */
+static vm_map_t rtR0MemObjFreeBSDGetMap(PRTR0MEMOBJINTERNAL pMem)
+{
+ switch (pMem->enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ return kernel_map;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ return NULL; /* pretend these have no mapping atm. */
+
+ case RTR0MEMOBJTYPE_LOCK:
+ return pMem->u.Lock.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.Lock.R0Process)->p_vmspace->vm_map;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ return pMem->u.ResVirt.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.ResVirt.R0Process)->p_vmspace->vm_map;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ return pMem->u.Mapping.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.Mapping.R0Process)->p_vmspace->vm_map;
+
+ default:
+ return NULL;
+ }
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)pMem;
+ int rc;
+
+ switch (pMemFreeBSD->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ rc = vm_map_remove(kernel_map,
+ (vm_offset_t)pMemFreeBSD->Core.pv,
+ (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x", rc));
+ break;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ {
+ vm_map_t pMap = kernel_map;
+
+ if (pMemFreeBSD->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ pMap = &((struct proc *)pMemFreeBSD->Core.u.Lock.R0Process)->p_vmspace->vm_map;
+
+ rc = vm_map_unwire(pMap,
+ (vm_offset_t)pMemFreeBSD->Core.pv,
+ (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb,
+ VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x", rc));
+ break;
+ }
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ {
+ vm_map_t pMap = kernel_map;
+ if (pMemFreeBSD->Core.u.ResVirt.R0Process != NIL_RTR0PROCESS)
+ pMap = &((struct proc *)pMemFreeBSD->Core.u.ResVirt.R0Process)->p_vmspace->vm_map;
+ rc = vm_map_remove(pMap,
+ (vm_offset_t)pMemFreeBSD->Core.pv,
+ (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x", rc));
+ break;
+ }
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ {
+ vm_map_t pMap = kernel_map;
+
+ if (pMemFreeBSD->Core.u.Mapping.R0Process != NIL_RTR0PROCESS)
+ pMap = &((struct proc *)pMemFreeBSD->Core.u.Mapping.R0Process)->p_vmspace->vm_map;
+ rc = vm_map_remove(pMap,
+ (vm_offset_t)pMemFreeBSD->Core.pv,
+ (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x", rc));
+ break;
+ }
+
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ {
+ VM_OBJECT_WLOCK(pMemFreeBSD->pObject);
+ vm_page_t pPage = vm_page_find_least(pMemFreeBSD->pObject, 0);
+#if __FreeBSD_version < 1000000
+ vm_page_lock_queues();
+#endif
+ for (vm_page_t pPage = vm_page_find_least(pMemFreeBSD->pObject, 0);
+ pPage != NULL;
+ pPage = vm_page_next(pPage))
+ {
+ vm_page_unwire(pPage, 0);
+ }
+#if __FreeBSD_version < 1000000
+ vm_page_unlock_queues();
+#endif
+ VM_OBJECT_WUNLOCK(pMemFreeBSD->pObject);
+ vm_object_deallocate(pMemFreeBSD->pObject);
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemFreeBSD->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static vm_page_t rtR0MemObjFreeBSDContigPhysAllocHelper(vm_object_t pObject, vm_pindex_t iPIndex,
+ u_long cPages, vm_paddr_t VmPhysAddrHigh,
+ u_long uAlignment, bool fWire)
+{
+ vm_page_t pPages;
+ int cTries = 0;
+
+#if __FreeBSD_version > 1000000
+ int fFlags = VM_ALLOC_INTERRUPT | VM_ALLOC_NOBUSY;
+ if (fWire)
+ fFlags |= VM_ALLOC_WIRED;
+
+ while (cTries <= 1)
+ {
+ VM_OBJECT_WLOCK(pObject);
+ pPages = vm_page_alloc_contig(pObject, iPIndex, fFlags, cPages, 0, VmPhysAddrHigh, uAlignment, 0, VM_MEMATTR_DEFAULT);
+ VM_OBJECT_WUNLOCK(pObject);
+ if (pPages)
+ break;
+#if __FreeBSD_version >= 1100092
+ if (!vm_page_reclaim_contig(cTries, cPages, 0, VmPhysAddrHigh, PAGE_SIZE, 0))
+ break;
+#else
+ vm_pageout_grow_cache(cTries, 0, VmPhysAddrHigh);
+#endif
+ cTries++;
+ }
+
+ return pPages;
+#else
+ while (cTries <= 1)
+ {
+ pPages = vm_phys_alloc_contig(cPages, 0, VmPhysAddrHigh, uAlignment, 0);
+ if (pPages)
+ break;
+ vm_contig_grow_cache(cTries, 0, VmPhysAddrHigh);
+ cTries++;
+ }
+
+ if (!pPages)
+ return pPages;
+ VM_OBJECT_WLOCK(pObject);
+ for (vm_pindex_t iPage = 0; iPage < cPages; iPage++)
+ {
+ vm_page_t pPage = pPages + iPage;
+ vm_page_insert(pPage, pObject, iPIndex + iPage);
+ pPage->valid = VM_PAGE_BITS_ALL;
+ if (fWire)
+ {
+ pPage->wire_count = 1;
+ atomic_add_int(&cnt.v_wire_count, 1);
+ }
+ }
+ VM_OBJECT_WUNLOCK(pObject);
+ return pPages;
+#endif
+}
+
+static int rtR0MemObjFreeBSDPhysAllocHelper(vm_object_t pObject, u_long cPages,
+ vm_paddr_t VmPhysAddrHigh, u_long uAlignment,
+ bool fContiguous, bool fWire, int rcNoMem)
+{
+ if (fContiguous)
+ {
+ if (rtR0MemObjFreeBSDContigPhysAllocHelper(pObject, 0, cPages, VmPhysAddrHigh, uAlignment, fWire) != NULL)
+ return VINF_SUCCESS;
+ return rcNoMem;
+ }
+
+ for (vm_pindex_t iPage = 0; iPage < cPages; iPage++)
+ {
+ vm_page_t pPage = rtR0MemObjFreeBSDContigPhysAllocHelper(pObject, iPage, 1, VmPhysAddrHigh, uAlignment, fWire);
+ if (pPage)
+ { /* likely */ }
+ else
+ {
+ /* Free all allocated pages */
+ VM_OBJECT_WLOCK(pObject);
+ while (iPage-- > 0)
+ {
+ pPage = vm_page_lookup(pObject, iPage);
+#if __FreeBSD_version < 1000000
+ vm_page_lock_queues();
+#endif
+ if (fWire)
+ vm_page_unwire(pPage, 0);
+ vm_page_free(pPage);
+#if __FreeBSD_version < 1000000
+ vm_page_unlock_queues();
+#endif
+ }
+ VM_OBJECT_WUNLOCK(pObject);
+ return rcNoMem;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+static int rtR0MemObjFreeBSDAllocHelper(PRTR0MEMOBJFREEBSD pMemFreeBSD, bool fExecutable,
+ vm_paddr_t VmPhysAddrHigh, bool fContiguous, int rcNoMem)
+{
+ vm_offset_t MapAddress = vm_map_min(kernel_map);
+ size_t cPages = atop(pMemFreeBSD->Core.cb);
+ int rc;
+
+ pMemFreeBSD->pObject = vm_object_allocate(OBJT_PHYS, cPages);
+
+ /* No additional object reference for auto-deallocation upon unmapping. */
+#if __FreeBSD_version >= 1000055
+ rc = vm_map_find(kernel_map, pMemFreeBSD->pObject, 0,
+ &MapAddress, pMemFreeBSD->Core.cb, 0, VMFS_ANY_SPACE,
+ fExecutable ? VM_PROT_ALL : VM_PROT_RW, VM_PROT_ALL, 0);
+#else
+ rc = vm_map_find(kernel_map, pMemFreeBSD->pObject, 0,
+ &MapAddress, pMemFreeBSD->Core.cb, VMFS_ANY_SPACE,
+ fExecutable ? VM_PROT_ALL : VM_PROT_RW, VM_PROT_ALL, 0);
+#endif
+
+ if (rc == KERN_SUCCESS)
+ {
+ rc = rtR0MemObjFreeBSDPhysAllocHelper(pMemFreeBSD->pObject, cPages, VmPhysAddrHigh, PAGE_SIZE,
+ fContiguous, false /*fWire*/, rcNoMem);
+ if (RT_SUCCESS(rc))
+ {
+ vm_map_wire(kernel_map, MapAddress, MapAddress + pMemFreeBSD->Core.cb, VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES);
+
+ /* Store start address */
+ pMemFreeBSD->Core.pv = (void *)MapAddress;
+ pMemFreeBSD->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ return VINF_SUCCESS;
+ }
+
+ vm_map_remove(kernel_map, MapAddress, MapAddress + pMemFreeBSD->Core.cb);
+ }
+ else
+ {
+ rc = rcNoMem; /** @todo fix translation (borrow from darwin) */
+ vm_object_deallocate(pMemFreeBSD->pObject);
+ }
+
+ rtR0MemObjDelete(&pMemFreeBSD->Core);
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_PAGE,
+ NULL, cb, pszTag);
+ if (pMemFreeBSD)
+ {
+ int rc = rtR0MemObjFreeBSDAllocHelper(pMemFreeBSD, fExecutable, ~(vm_paddr_t)0, false /*fContiguous*/, VERR_NO_MEMORY);
+ if (RT_SUCCESS(rc))
+ *ppMem = &pMemFreeBSD->Core;
+ else
+ rtR0MemObjDelete(&pMemFreeBSD->Core);
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_LOW, NULL, cb, pszTag);
+ if (pMemFreeBSD)
+ {
+ int rc = rtR0MemObjFreeBSDAllocHelper(pMemFreeBSD, fExecutable, _4G - 1, false /*fContiguous*/, VERR_NO_LOW_MEMORY);
+ if (RT_SUCCESS(rc))
+ *ppMem = &pMemFreeBSD->Core;
+ else
+ rtR0MemObjDelete(&pMemFreeBSD->Core);
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_CONT,
+ NULL, cb, pszTag);
+ if (pMemFreeBSD)
+ {
+ int rc = rtR0MemObjFreeBSDAllocHelper(pMemFreeBSD, fExecutable, _4G - 1, true /*fContiguous*/, VERR_NO_CONT_MEMORY);
+ if (RT_SUCCESS(rc))
+ {
+ pMemFreeBSD->Core.u.Cont.Phys = vtophys(pMemFreeBSD->Core.pv);
+ *ppMem = &pMemFreeBSD->Core;
+ }
+ else
+ rtR0MemObjDelete(&pMemFreeBSD->Core);
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+static int rtR0MemObjFreeBSDAllocPhysPages(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJTYPE enmType, size_t cb, RTHCPHYS PhysHighest,
+ size_t uAlignment, bool fContiguous, int rcNoMem, const char *pszTag)
+{
+ /* create the object. */
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), enmType, NULL, cb, pszTag);
+ if (pMemFreeBSD)
+ {
+ vm_paddr_t const VmPhysAddrHigh = PhysHighest != NIL_RTHCPHYS ? PhysHighest : ~(vm_paddr_t)0;
+ u_long const cPages = atop(cb);
+
+ pMemFreeBSD->pObject = vm_object_allocate(OBJT_PHYS, cPages);
+
+ int rc = rtR0MemObjFreeBSDPhysAllocHelper(pMemFreeBSD->pObject, cPages, VmPhysAddrHigh,
+ uAlignment, fContiguous, true, rcNoMem);
+ if (RT_SUCCESS(rc))
+ {
+ if (fContiguous)
+ {
+ Assert(enmType == RTR0MEMOBJTYPE_PHYS);
+ VM_OBJECT_WLOCK(pMemFreeBSD->pObject);
+ pMemFreeBSD->Core.u.Phys.PhysBase = VM_PAGE_TO_PHYS(vm_page_find_least(pMemFreeBSD->pObject, 0));
+ VM_OBJECT_WUNLOCK(pMemFreeBSD->pObject);
+ pMemFreeBSD->Core.u.Phys.fAllocated = true;
+ }
+
+ pMemFreeBSD->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ *ppMem = &pMemFreeBSD->Core;
+ }
+ else
+ {
+ vm_object_deallocate(pMemFreeBSD->pObject);
+ rtR0MemObjDelete(&pMemFreeBSD->Core);
+ }
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment,
+ const char *pszTag)
+{
+ return rtR0MemObjFreeBSDAllocPhysPages(ppMem, RTR0MEMOBJTYPE_PHYS, cb, PhysHighest, uAlignment, true, VERR_NO_MEMORY, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ return rtR0MemObjFreeBSDAllocPhysPages(ppMem, RTR0MEMOBJTYPE_PHYS_NC, cb, PhysHighest, PAGE_SIZE, false,
+ VERR_NO_PHYS_MEMORY, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy,
+ const char *pszTag)
+{
+ AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE, VERR_NOT_SUPPORTED);
+
+ /* create the object. */
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_PHYS,
+ NULL, cb, pszTag);
+ if (pMemFreeBSD)
+ {
+ /* there is no allocation here, it needs to be mapped somewhere first. */
+ pMemFreeBSD->Core.u.Phys.fAllocated = false;
+ pMemFreeBSD->Core.u.Phys.PhysBase = Phys;
+ pMemFreeBSD->Core.u.Phys.uCachePolicy = uCachePolicy;
+ *ppMem = &pMemFreeBSD->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Worker locking the memory in either kernel or user maps.
+ */
+static int rtR0MemObjNativeLockInMap(PPRTR0MEMOBJINTERNAL ppMem, vm_map_t pVmMap,
+ vm_offset_t AddrStart, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, int fFlags, const char *pszTag)
+{
+ int rc;
+ NOREF(fAccess);
+
+ /* create the object. */
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_LOCK,
+ (void *)AddrStart, cb, pszTag);
+ if (!pMemFreeBSD)
+ return VERR_NO_MEMORY;
+
+ /*
+ * We could've used vslock here, but we don't wish to be subject to
+ * resource usage restrictions, so we'll call vm_map_wire directly.
+ */
+ rc = vm_map_wire(pVmMap, /* the map */
+ AddrStart, /* start */
+ AddrStart + cb, /* end */
+ fFlags); /* flags */
+ if (rc == KERN_SUCCESS)
+ {
+ pMemFreeBSD->Core.u.Lock.R0Process = R0Process;
+ *ppMem = &pMemFreeBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemFreeBSD->Core);
+ return VERR_NO_MEMORY;/** @todo fix mach -> vbox error conversion for freebsd. */
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ return rtR0MemObjNativeLockInMap(ppMem,
+ &((struct proc *)R0Process)->p_vmspace->vm_map,
+ (vm_offset_t)R3Ptr,
+ cb,
+ fAccess,
+ R0Process,
+ VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES,
+ pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ return rtR0MemObjNativeLockInMap(ppMem,
+ kernel_map,
+ (vm_offset_t)pv,
+ cb,
+ fAccess,
+ NIL_RTR0PROCESS,
+ VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES,
+ pszTag);
+}
+
+
+/**
+ * Worker for the two virtual address space reservers.
+ *
+ * We're leaning on the examples provided by mmap and vm_mmap in vm_mmap.c here.
+ */
+static int rtR0MemObjNativeReserveInMap(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, vm_map_t pMap, const char *pszTag)
+{
+ int rc;
+
+ /*
+ * The pvFixed address range must be within the VM space when specified.
+ */
+ if ( pvFixed != (void *)-1
+ && ( (vm_offset_t)pvFixed < vm_map_min(pMap)
+ || (vm_offset_t)pvFixed + cb > vm_map_max(pMap)))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Check that the specified alignment is supported.
+ */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Create the object.
+ */
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_RES_VIRT,
+ NULL, cb, pszTag);
+ if (!pMemFreeBSD)
+ return VERR_NO_MEMORY;
+
+ vm_offset_t MapAddress = pvFixed != (void *)-1
+ ? (vm_offset_t)pvFixed
+ : vm_map_min(pMap);
+ if (pvFixed != (void *)-1)
+ vm_map_remove(pMap,
+ MapAddress,
+ MapAddress + cb);
+
+ rc = vm_map_find(pMap, /* map */
+ NULL, /* object */
+ 0, /* offset */
+ &MapAddress, /* addr (IN/OUT) */
+ cb, /* length */
+#if __FreeBSD_version >= 1000055
+ 0, /* max addr */
+#endif
+ pvFixed == (void *)-1 ? VMFS_ANY_SPACE : VMFS_NO_SPACE,
+ /* find_space */
+ VM_PROT_NONE, /* protection */
+ VM_PROT_ALL, /* max(_prot) ?? */
+ 0); /* cow (copy-on-write) */
+ if (rc == KERN_SUCCESS)
+ {
+ if (R0Process != NIL_RTR0PROCESS)
+ {
+ rc = vm_map_inherit(pMap,
+ MapAddress,
+ MapAddress + cb,
+ VM_INHERIT_SHARE);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc));
+ }
+ pMemFreeBSD->Core.pv = (void *)MapAddress;
+ pMemFreeBSD->Core.u.ResVirt.R0Process = R0Process;
+ *ppMem = &pMemFreeBSD->Core;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY; /** @todo fix translation (borrow from darwin) */
+ rtR0MemObjDelete(&pMemFreeBSD->Core);
+ return rc;
+
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ const char *pszTag)
+{
+ return rtR0MemObjNativeReserveInMap(ppMem, pvFixed, cb, uAlignment, NIL_RTR0PROCESS, kernel_map, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ return rtR0MemObjNativeReserveInMap(ppMem, (void *)R3PtrFixed, cb, uAlignment, R0Process,
+ &((struct proc *)R0Process)->p_vmspace->vm_map, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+// AssertMsgReturn(!offSub && !cbSub, ("%#x %#x\n", offSub, cbSub), VERR_NOT_SUPPORTED);
+ AssertMsgReturn(pvFixed == (void *)-1, ("%p\n", pvFixed), VERR_NOT_SUPPORTED);
+
+ /*
+ * Check that the specified alignment is supported.
+ */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+ Assert(!offSub || cbSub);
+
+ int rc;
+ PRTR0MEMOBJFREEBSD pMemToMapFreeBSD = (PRTR0MEMOBJFREEBSD)pMemToMap;
+
+ /* calc protection */
+ vm_prot_t ProtectionFlags = 0;
+ if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE)
+ ProtectionFlags = VM_PROT_NONE;
+ if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ)
+ ProtectionFlags |= VM_PROT_READ;
+ if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE)
+ ProtectionFlags |= VM_PROT_WRITE;
+ if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC)
+ ProtectionFlags |= VM_PROT_EXECUTE;
+
+ vm_offset_t Addr = vm_map_min(kernel_map);
+ if (cbSub == 0)
+ cbSub = pMemToMap->cb - offSub;
+
+ vm_object_reference(pMemToMapFreeBSD->pObject);
+ rc = vm_map_find(kernel_map, /* Map to insert the object in */
+ pMemToMapFreeBSD->pObject, /* Object to map */
+ offSub, /* Start offset in the object */
+ &Addr, /* Start address IN/OUT */
+ cbSub, /* Size of the mapping */
+#if __FreeBSD_version >= 1000055
+ 0, /* Upper bound of mapping */
+#endif
+ VMFS_ANY_SPACE, /* Whether a suitable address should be searched for first */
+ ProtectionFlags, /* protection flags */
+ VM_PROT_ALL, /* Maximum protection flags */
+ 0); /* copy-on-write and similar flags */
+
+ if (rc == KERN_SUCCESS)
+ {
+ rc = vm_map_wire(kernel_map, Addr, Addr + cbSub, VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc));
+
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(RTR0MEMOBJFREEBSD), RTR0MEMOBJTYPE_MAPPING,
+ (void *)Addr, cbSub, pszTag);
+ if (pMemFreeBSD)
+ {
+ Assert((vm_offset_t)pMemFreeBSD->Core.pv == Addr);
+ pMemFreeBSD->Core.u.Mapping.R0Process = NIL_RTR0PROCESS;
+ *ppMem = &pMemFreeBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rc = vm_map_remove(kernel_map, Addr, Addr + cbSub);
+ AssertMsg(rc == KERN_SUCCESS, ("Deleting mapping failed\n"));
+ }
+ else
+ vm_object_deallocate(pMemToMapFreeBSD->pObject);
+
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ /*
+ * Check for unsupported stuff.
+ */
+ AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED);
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+ Assert(!offSub || cbSub);
+
+ int rc;
+ PRTR0MEMOBJFREEBSD pMemToMapFreeBSD = (PRTR0MEMOBJFREEBSD)pMemToMap;
+ struct proc *pProc = (struct proc *)R0Process;
+ struct vm_map *pProcMap = &pProc->p_vmspace->vm_map;
+
+ /* calc protection */
+ vm_prot_t ProtectionFlags = 0;
+ if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE)
+ ProtectionFlags = VM_PROT_NONE;
+ if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ)
+ ProtectionFlags |= VM_PROT_READ;
+ if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE)
+ ProtectionFlags |= VM_PROT_WRITE;
+ if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC)
+ ProtectionFlags |= VM_PROT_EXECUTE;
+
+ /* calc mapping address */
+ vm_offset_t AddrR3;
+ if (R3PtrFixed == (RTR3PTR)-1)
+ {
+ /** @todo is this needed?. */
+ PROC_LOCK(pProc);
+ AddrR3 = round_page((vm_offset_t)pProc->p_vmspace->vm_daddr + MY_LIM_MAX_PROC(pProc, RLIMIT_DATA));
+ PROC_UNLOCK(pProc);
+ }
+ else
+ AddrR3 = (vm_offset_t)R3PtrFixed;
+
+ if (cbSub == 0)
+ cbSub = pMemToMap->cb - offSub;
+
+ /* Insert the pObject in the map. */
+ vm_object_reference(pMemToMapFreeBSD->pObject);
+ rc = vm_map_find(pProcMap, /* Map to insert the object in */
+ pMemToMapFreeBSD->pObject, /* Object to map */
+ offSub, /* Start offset in the object */
+ &AddrR3, /* Start address IN/OUT */
+ cbSub, /* Size of the mapping */
+#if __FreeBSD_version >= 1000055
+ 0, /* Upper bound of the mapping */
+#endif
+ R3PtrFixed == (RTR3PTR)-1 ? VMFS_ANY_SPACE : VMFS_NO_SPACE,
+ /* Whether a suitable address should be searched for first */
+ ProtectionFlags, /* protection flags */
+ VM_PROT_ALL, /* Maximum protection flags */
+ 0); /* copy-on-write and similar flags */
+
+ if (rc == KERN_SUCCESS)
+ {
+ rc = vm_map_wire(pProcMap, AddrR3, AddrR3 + pMemToMap->cb, VM_MAP_WIRE_USER|VM_MAP_WIRE_NOHOLES);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc));
+
+ rc = vm_map_inherit(pProcMap, AddrR3, AddrR3 + pMemToMap->cb, VM_INHERIT_SHARE);
+ AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc));
+
+ /*
+ * Create a mapping object for it.
+ */
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(RTR0MEMOBJFREEBSD), RTR0MEMOBJTYPE_MAPPING,
+ (void *)AddrR3, pMemToMap->cb, pszTag);
+ if (pMemFreeBSD)
+ {
+ Assert((vm_offset_t)pMemFreeBSD->Core.pv == AddrR3);
+ pMemFreeBSD->Core.u.Mapping.R0Process = R0Process;
+ *ppMem = &pMemFreeBSD->Core;
+ return VINF_SUCCESS;
+ }
+
+ rc = vm_map_remove(pProcMap, AddrR3, AddrR3 + pMemToMap->cb);
+ AssertMsg(rc == KERN_SUCCESS, ("Deleting mapping failed\n"));
+ }
+ else
+ vm_object_deallocate(pMemToMapFreeBSD->pObject);
+
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ vm_prot_t ProtectionFlags = 0;
+ vm_offset_t AddrStart = (uintptr_t)pMem->pv + offSub;
+ vm_offset_t AddrEnd = AddrStart + cbSub;
+ vm_map_t pVmMap = rtR0MemObjFreeBSDGetMap(pMem);
+
+ if (!pVmMap)
+ return VERR_NOT_SUPPORTED;
+
+ if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE)
+ ProtectionFlags = VM_PROT_NONE;
+ if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ)
+ ProtectionFlags |= VM_PROT_READ;
+ if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE)
+ ProtectionFlags |= VM_PROT_WRITE;
+ if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC)
+ ProtectionFlags |= VM_PROT_EXECUTE;
+
+ int krc = vm_map_protect(pVmMap, AddrStart, AddrEnd, ProtectionFlags, FALSE);
+ if (krc == KERN_SUCCESS)
+ return VINF_SUCCESS;
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)pMem;
+
+ switch (pMemFreeBSD->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_LOCK:
+ {
+ if ( pMemFreeBSD->Core.u.Lock.R0Process != NIL_RTR0PROCESS
+ && pMemFreeBSD->Core.u.Lock.R0Process != (RTR0PROCESS)curproc)
+ {
+ /* later */
+ return NIL_RTHCPHYS;
+ }
+
+ vm_offset_t pb = (vm_offset_t)pMemFreeBSD->Core.pv + ptoa(iPage);
+
+ struct proc *pProc = (struct proc *)pMemFreeBSD->Core.u.Lock.R0Process;
+ struct vm_map *pProcMap = &pProc->p_vmspace->vm_map;
+ pmap_t pPhysicalMap = vm_map_pmap(pProcMap);
+
+ return pmap_extract(pPhysicalMap, pb);
+ }
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ {
+ vm_offset_t pb = (vm_offset_t)pMemFreeBSD->Core.pv + ptoa(iPage);
+
+ if (pMemFreeBSD->Core.u.Mapping.R0Process != NIL_RTR0PROCESS)
+ {
+ struct proc *pProc = (struct proc *)pMemFreeBSD->Core.u.Mapping.R0Process;
+ struct vm_map *pProcMap = &pProc->p_vmspace->vm_map;
+ pmap_t pPhysicalMap = vm_map_pmap(pProcMap);
+
+ return pmap_extract(pPhysicalMap, pb);
+ }
+ return vtophys(pb);
+ }
+
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ {
+ RTHCPHYS addr;
+
+ VM_OBJECT_WLOCK(pMemFreeBSD->pObject);
+ addr = VM_PAGE_TO_PHYS(vm_page_lookup(pMemFreeBSD->pObject, iPage));
+ VM_OBJECT_WUNLOCK(pMemFreeBSD->pObject);
+ return addr;
+ }
+
+ case RTR0MEMOBJTYPE_PHYS:
+ return pMemFreeBSD->Core.u.Cont.Phys + ptoa(iPage);
+
+ case RTR0MEMOBJTYPE_CONT:
+ return pMemFreeBSD->Core.u.Phys.PhysBase + ptoa(iPage);
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ default:
+ return NIL_RTHCPHYS;
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c
new file mode 100644
index 00000000..940172ab
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c
@@ -0,0 +1,93 @@
+/* $Id: memuserkernel-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - User & Kernel Memory, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * 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-freebsd-kernel.h"
+
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+
+
+RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb)
+{
+ int rc = copyin((const void *)R3PtrSrc, pvDst, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb)
+{
+ int rc = copyout(pvSrc, (void *)R3PtrDst, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr)
+{
+ return R3Ptr < VM_MAXUSER_ADDRESS;
+}
+
+
+RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv)
+{
+ return (uintptr_t)pv >= VM_MAXUSER_ADDRESS;
+}
+
+
+RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void)
+{
+ return true;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c
new file mode 100644
index 00000000..0605fd05
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c
@@ -0,0 +1,322 @@
+/* $Id: mp-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * 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 "the-freebsd-kernel.h"
+
+#include <iprt/mp.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/cpuset.h>
+#include "r0drv/mp-r0drv.h"
+
+
+#if __FreeBSD_version < 1200028
+# define smp_no_rendezvous_barrier smp_no_rendevous_barrier
+#endif
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ return curcpu;
+}
+
+
+RTDECL(int) RTMpCurSetIndex(void)
+{
+ return curcpu;
+}
+
+
+RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu)
+{
+ return *pidCpu = curcpu;
+}
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS && idCpu <= mp_maxid ? (int)idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu <= mp_maxid ? (RTCPUID)iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return mp_maxid;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return idCpu <= mp_maxid;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ return mp_maxid + 1;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCoreCount(void)
+{
+ return mp_maxid + 1;
+}
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ return idCpu <= mp_maxid
+ && !CPU_ABSENT(idCpu);
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ return mp_ncpus;
+}
+
+
+/**
+ * Wrapper between the native FreeBSD per-cpu callback and PFNRTWORKER
+ * for the RTMpOnAll API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnAllFreeBSDWrapper(void *pvArg)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ pArgs->pfnWorker(curcpu, pArgs->pvUser1, pArgs->pvUser2);
+}
+
+
+RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RTMPARGS Args;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = NIL_RTCPUID;
+ Args.cHits = 0;
+ smp_rendezvous(NULL, rtmpOnAllFreeBSDWrapper, smp_no_rendezvous_barrier, &Args);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native FreeBSD per-cpu callback and PFNRTWORKER
+ * for the RTMpOnOthers API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnOthersFreeBSDWrapper(void *pvArg)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ RTCPUID idCpu = curcpu;
+ if (pArgs->idCpu != idCpu)
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+}
+
+
+RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ /* Will panic if no rendezvousing cpus, so check up front. */
+ if (RTMpGetOnlineCount() > 1)
+ {
+#if __FreeBSD_version >= 900000
+ cpuset_t Mask;
+#elif __FreeBSD_version >= 700000
+ cpumask_t Mask;
+#endif
+ RTMPARGS Args;
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = RTMpCpuId();
+ Args.cHits = 0;
+#if __FreeBSD_version >= 700000
+# if __FreeBSD_version >= 900000
+ Mask = all_cpus;
+ CPU_CLR(curcpu, &Mask);
+# else
+ Mask = ~(cpumask_t)curcpu;
+# endif
+ smp_rendezvous_cpus(Mask, NULL, rtmpOnOthersFreeBSDWrapper, smp_no_rendezvous_barrier, &Args);
+#else
+ smp_rendezvous(NULL, rtmpOnOthersFreeBSDWrapper, NULL, &Args);
+#endif
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native FreeBSD per-cpu callback and PFNRTWORKER
+ * for the RTMpOnSpecific API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnSpecificFreeBSDWrapper(void *pvArg)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ RTCPUID idCpu = curcpu;
+ if (pArgs->idCpu == idCpu)
+ {
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+ }
+}
+
+
+RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+#if __FreeBSD_version >= 900000
+ cpuset_t Mask;
+#elif __FreeBSD_version >= 700000
+ cpumask_t Mask;
+#endif
+ RTMPARGS Args;
+
+ /* Will panic if no rendezvousing cpus, so make sure the cpu is online. */
+ if (!RTMpIsCpuOnline(idCpu))
+ return VERR_CPU_NOT_FOUND;
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu;
+ Args.cHits = 0;
+#if __FreeBSD_version >= 700000
+# if __FreeBSD_version >= 900000
+ CPU_SETOF(idCpu, &Mask);
+# else
+ Mask = (cpumask_t)1 << idCpu;
+# endif
+ smp_rendezvous_cpus(Mask, NULL, rtmpOnSpecificFreeBSDWrapper, smp_no_rendezvous_barrier, &Args);
+#else
+ smp_rendezvous(NULL, rtmpOnSpecificFreeBSDWrapper, NULL, &Args);
+#endif
+ return Args.cHits == 1
+ ? VINF_SUCCESS
+ : VERR_CPU_NOT_FOUND;
+}
+
+
+#if __FreeBSD_version >= 700000
+/**
+ * Dummy callback for RTMpPokeCpu.
+ * @param pvArg Ignored
+ */
+static void rtmpFreeBSDPokeCallback(void *pvArg)
+{
+ NOREF(pvArg);
+}
+
+
+RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
+{
+#if __FreeBSD_version >= 900000
+ cpuset_t Mask;
+#elif __FreeBSD_version >= 700000
+ cpumask_t Mask;
+#endif
+
+ /* Will panic if no rendezvousing cpus, so make sure the cpu is online. */
+ if (!RTMpIsCpuOnline(idCpu))
+ return VERR_CPU_NOT_FOUND;
+
+# if __FreeBSD_version >= 900000
+ CPU_SETOF(idCpu, &Mask);
+# else
+ Mask = (cpumask_t)1 << idCpu;
+# endif
+ smp_rendezvous_cpus(Mask, NULL, rtmpFreeBSDPokeCallback, smp_no_rendezvous_barrier, NULL);
+
+ return VINF_SUCCESS;
+}
+
+#else /* < 7.0 */
+RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
+{
+ return VERR_NOT_SUPPORTED;
+}
+#endif /* < 7.0 */
+
+
+RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
+{
+ return true;
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c
new file mode 100644
index 00000000..eab3058d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c
@@ -0,0 +1,85 @@
+/* $Id: process-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Process Management, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+
+#include <iprt/process.h>
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ struct proc *pSelf = curproc;
+ return pSelf->p_pid;
+}
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ return (RTR0PROCESS)curproc;
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c
new file mode 100644
index 00000000..b9ddcf6c
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c
@@ -0,0 +1,297 @@
+/* $Id: semevent-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Single Release Event Semaphores, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 RTSEMEVENT_WITHOUT_REMAPPING
+#include "the-freebsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+
+#include "sleepqueue-r0drv-freebsd.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * FreeBSD event semaphore.
+ */
+typedef struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The object status - !0 when signaled and 0 when reset. */
+ uint32_t volatile fState;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+ AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
+
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->fState = 0;
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Retains a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventBsdRetain(PRTSEMEVENTINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < 100000); NOREF(cRefs);
+}
+
+
+/**
+ * Releases a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventBsdRelease(PRTSEMEVENTINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ RTMemFree(pThis);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC);
+ ASMAtomicWriteU32(&pThis->fState, 0);
+ rtR0SemBsdBroadcast(pThis);
+ rtR0SemEventBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ rtR0SemEventBsdRetain(pThis);
+
+ /*
+ * Signal the event object.
+ */
+ ASMAtomicWriteU32(&pThis->fState, 1);
+ rtR0SemBsdSignal(pThis);
+ rtR0SemEventBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ int rc;
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ rtR0SemEventBsdRetain(pThis);
+
+ /*
+ * Try grab the event without setting up the wait.
+ */
+ if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1))
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ RTR0SEMBSDSLEEP Wait;
+ rc = rtR0SemBsdWaitInit(&Wait, fFlags, uTimeout, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ rtR0SemBsdWaitPrepare(&Wait);
+
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1))
+ rc = VINF_SUCCESS;
+ else if (rtR0SemBsdWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemBsdWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemBsdWaitDoIt(&Wait);
+ continue;
+ }
+ }
+ break;
+ }
+
+ rtR0SemBsdWaitDelete(&Wait);
+ }
+ }
+
+ rtR0SemEventBsdRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitEx);
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ return 1000000000 / hz;
+}
+
+
+RTR0DECL(bool) RTSemEventIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c
new file mode 100644
index 00000000..d8a83ba5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c
@@ -0,0 +1,362 @@
+/* $Id: semeventmulti-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#include "the-freebsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/lockvalidator.h>
+
+#include "sleepqueue-r0drv-freebsd.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name fStateAndGen values
+ * @{ */
+/** The state bit number. */
+#define RTSEMEVENTMULTIBSD_STATE_BIT 0
+/** The state mask. */
+#define RTSEMEVENTMULTIBSD_STATE_MASK RT_BIT_32(RTSEMEVENTMULTIBSD_STATE_BIT)
+/** The generation mask. */
+#define RTSEMEVENTMULTIBSD_GEN_MASK ~RTSEMEVENTMULTIBSD_STATE_MASK
+/** The generation shift. */
+#define RTSEMEVENTMULTIBSD_GEN_SHIFT 1
+/** The initial variable value. */
+#define RTSEMEVENTMULTIBSD_STATE_GEN_INIT UINT32_C(0xfffffffc)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * FreeBSD multiple release event semaphore.
+ */
+typedef struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The object state bit and generation counter.
+ * The generation counter is incremented every time the object is
+ * signalled. */
+ uint32_t volatile fStateAndGen;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis;
+
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->fStateAndGen = RTSEMEVENTMULTIBSD_STATE_GEN_INIT;
+ pThis->cRefs = 1;
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiBsdRetain(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiBsdRelease(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ {
+ Assert(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC);
+ RTMemFree(pThis);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC);
+ ASMAtomicAndU32(&pThis->fStateAndGen, RTSEMEVENTMULTIBSD_GEN_MASK);
+ rtR0SemBsdBroadcast(pThis);
+ rtR0SemEventMultiBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ uint32_t fNew;
+ uint32_t fOld;
+
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiBsdRetain(pThis);
+
+ /*
+ * Signal the event object. The cause of the parnoia here is racing to try
+ * deal with racing RTSemEventMultiSignal calls (should probably be
+ * forbidden, but it's relatively easy to handle).
+ */
+ do
+ {
+ fNew = fOld = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ fNew += 1 << RTSEMEVENTMULTIBSD_GEN_SHIFT;
+ fNew |= RTSEMEVENTMULTIBSD_STATE_MASK;
+ }
+ while (!ASMAtomicCmpXchgU32(&pThis->fStateAndGen, fNew, fOld));
+
+ rtR0SemBsdBroadcast(pThis);
+ rtR0SemEventMultiBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiBsdRetain(pThis);
+
+ /*
+ * Reset it.
+ */
+ ASMAtomicAndU32(&pThis->fStateAndGen, ~RTSEMEVENTMULTIBSD_STATE_MASK);
+
+ rtR0SemEventMultiBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param uTimeout See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventMultiBsdWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ uint32_t fOrgStateAndGen;
+ int rc;
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiBsdRetain(pThis);
+
+ /*
+ * Is the event already signalled or do we have to wait?
+ */
+ fOrgStateAndGen = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ if (fOrgStateAndGen & RTSEMEVENTMULTIBSD_STATE_MASK)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ RTR0SEMBSDSLEEP Wait;
+ rc = rtR0SemBsdWaitInit(&Wait, fFlags, uTimeout, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ rtR0SemBsdWaitPrepare(&Wait);
+
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (ASMAtomicUoReadU32(&pThis->fStateAndGen) != fOrgStateAndGen)
+ rc = VINF_SUCCESS;
+ else if (rtR0SemBsdWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemBsdWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemBsdWaitDoIt(&Wait);
+ continue;
+ }
+ }
+ break;
+ }
+
+ rtR0SemBsdWaitDelete(&Wait);
+ }
+ }
+
+ rtR0SemEventMultiBsdRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitEx);
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventMultiGetResolution(void)
+{
+ return rtR0SemBsdWaitGetResolution();
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiGetResolution);
+
+
+RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiIsSignalSafe);
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c
new file mode 100644
index 00000000..a3e85f45
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c
@@ -0,0 +1,149 @@
+/* $Id: semfastmutex-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/errcore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the FreeBSD (sleep) mutex.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The FreeBSD shared/exclusive lock mutex. */
+ struct sx SxLock;
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ AssertCompile(sizeof(RTSEMFASTMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phFastMtx, VERR_INVALID_POINTER);
+
+ PRTSEMFASTMUTEXINTERNAL pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ sx_init_flags(&pThis->SxLock, "IPRT Fast Mutex Semaphore", SX_DUPOK);
+
+ *phFastMtx = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ sx_destroy(&pThis->SxLock);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ sx_xlock(&pThis->SxLock);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ sx_xunlock(&pThis->SxLock);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c
new file mode 100644
index 00000000..9d87ccb3
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c
@@ -0,0 +1,229 @@
+/* $Id: semmutex-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Mutex Semaphores, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTSEMMUTEX_WITHOUT_REMAPPING
+#include "the-freebsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the FreeBSD (sleep) mutex.
+ */
+typedef struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The FreeBSD shared/exclusive lock mutex. */
+ struct sx SxLock;
+} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ AssertCompile(sizeof(RTSEMMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phMutexSem, VERR_INVALID_POINTER);
+
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ sx_init_flags(&pThis->SxLock, "IPRT Mutex Semaphore", SX_RECURSE);
+
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+
+ sx_destroy(&pThis->SxLock);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ int rc;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ sx_xlock(&pThis->SxLock);
+ rc = VINF_SUCCESS;
+ }
+ else if (!cMillies)
+ {
+ if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_TIMEOUT;
+ }
+ /*
+ * GROSS HACK: poll implementation of timeout.
+ */
+ /** @todo Implement timeouts in RTSemMutexRequest. */
+ else if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ {
+ uint64_t StartTS = RTTimeSystemMilliTS();
+ rc = VERR_TIMEOUT;
+ do
+ {
+ RTThreadSleep(1);
+ if (sx_try_xlock(&pThis->SxLock))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ } while (RTTimeSystemMilliTS() - StartTS < cMillies);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequest(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ int rc;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ if (!sx_xlock_sig(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_INTERRUPTED;
+ }
+ else if (!cMillies)
+ {
+ if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_TIMEOUT;
+ }
+ /*
+ * GROSS HACK: poll implementation of timeout.
+ */
+ /** @todo Implement timeouts and interrupt checks in
+ * RTSemMutexRequestNoResume. */
+ else if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ {
+ uint64_t StartTS = RTTimeSystemMilliTS();
+ rc = VERR_TIMEOUT;
+ do
+ {
+ RTThreadSleep(1);
+ if (sx_try_xlock(&pThis->SxLock))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ } while (RTTimeSystemMilliTS() - StartTS < cMillies);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ sx_xunlock(&pThis->SxLock);
+ return VINF_SUCCESS;
+}
+
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), false);
+
+ return sx_xlocked(&pThis->SxLock);
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h b/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h
new file mode 100644
index 00000000..a16fbae6
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h
@@ -0,0 +1,344 @@
+/* $Id: sleepqueue-r0drv-freebsd.h $ */
+/** @file
+ * IPRT - FreeBSD Ring-0 Driver Helpers for Abstracting Sleep Queues,
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h
+#define IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "the-freebsd-kernel.h"
+
+#include <iprt/asm-math.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+/**
+ * Kernel mode FreeBSD wait state structure.
+ */
+typedef struct RTR0SEMBSDSLEEP
+{
+ /** The absolute timeout given as nano seconds since the start of the
+ * monotonic clock. */
+ uint64_t uNsAbsTimeout;
+ /** The timeout in ticks. Updated after waiting. */
+ int iTimeout;
+ /** Set if it's an indefinite wait. */
+ bool fIndefinite;
+ /** Set if we've already timed out.
+ * Set by rtR0SemBsdWaitDoIt and read by rtR0SemBsdWaitHasTimedOut. */
+ bool fTimedOut;
+ /** Flag whether the wait was interrupted. */
+ bool fInterrupted;
+ /** flag whether the wait is interruptible or not. */
+ bool fInterruptible;
+ /** Opaque wait channel id. */
+ void *pvWaitChan;
+} RTR0SEMBSDSLEEP;
+/** Pointer to a FreeBSD wait state. */
+typedef RTR0SEMBSDSLEEP *PRTR0SEMBSDSLEEP;
+
+
+/**
+ * Updates the timeout of the FreeBSD wait.
+ *
+ * @returns RTSEMWAIT_FLAGS_INDEFINITE if the timeout value is too big.
+ * 0 otherwise
+ * @param pWait The wait structure.
+ * @param uTimeout The relative timeout in nanoseconds.
+ */
+DECLINLINE(uint32_t) rtR0SemBsdWaitUpdateTimeout(PRTR0SEMBSDSLEEP pWait, uint64_t uTimeout)
+{
+#if 0
+ struct timeval tv;
+
+ tv.tv_sec = uTimeout / UINT64_C(1000000000);
+ tv.tv_usec = (uTimeout % UINT64_C(1000000000)) / UINT64_C(1000);
+
+ pWait->iTimeout = tvtohz(&tv);
+#else
+ uint64_t cTicks = ASMMultU64ByU32DivByU32(uTimeout, hz, UINT32_C(1000000000));
+ if (cTicks >= INT_MAX)
+ return RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ pWait->iTimeout = (int)cTicks;
+#endif
+
+ return 0;
+}
+
+/**
+ * Initializes a wait.
+ *
+ * The caller MUST check the wait condition BEFORE calling this function or the
+ * timeout logic will be flawed.
+ *
+ * @returns VINF_SUCCESS or VERR_TIMEOUT.
+ * @param pWait The wait structure.
+ * @param fFlags The wait flags.
+ * @param uTimeout The timeout.
+ * @param pvWaitChan The opaque wait channel.
+ */
+DECLINLINE(int) rtR0SemBsdWaitInit(PRTR0SEMBSDSLEEP pWait, uint32_t fFlags, uint64_t uTimeout,
+ void *pvWaitChan)
+{
+ pWait->iTimeout = 0;
+ pWait->uNsAbsTimeout = 0; /* shut up gcc */
+
+ /*
+ * Process the flags and timeout.
+ */
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+/** @todo optimize: millisecs -> nanosecs -> millisec -> jiffies */
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000)
+ ? uTimeout * UINT32_C(1000000)
+ : UINT64_MAX;
+ if (uTimeout == UINT64_MAX)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ {
+ uint64_t u64Now;
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ {
+ if (uTimeout == 0)
+ return VERR_TIMEOUT;
+
+ u64Now = RTTimeSystemNanoTS();
+ if (u64Now + uTimeout < u64Now) /* overflow */
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ pWait->uNsAbsTimeout = u64Now + uTimeout;
+ }
+ else
+ {
+ u64Now = RTTimeSystemNanoTS();
+ if (u64Now >= uTimeout)
+ return VERR_TIMEOUT;
+
+ pWait->uNsAbsTimeout = uTimeout;
+ uTimeout -= u64Now; /* Get a relative value. */
+ }
+ }
+ }
+
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+ pWait->fIndefinite = false;
+ fFlags |= rtR0SemBsdWaitUpdateTimeout(pWait, uTimeout);
+ }
+
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ {
+ pWait->fIndefinite = true;
+ pWait->iTimeout = INT_MAX;
+ pWait->uNsAbsTimeout = UINT64_MAX;
+ }
+
+ pWait->fTimedOut = false;
+
+ /*
+ * Initialize the wait queue related bits.
+ */
+ pWait->fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE
+ ? true : false;
+ pWait->pvWaitChan = pvWaitChan;
+ pWait->fInterrupted = false;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Prepares the next wait.
+ *
+ * This must be called before rtR0SemBsdWaitDoIt, and the caller should check
+ * the exit conditions inbetween the two calls.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemBsdWaitPrepare(PRTR0SEMBSDSLEEP pWait)
+{
+ /* Lock the queues. */
+ sleepq_lock(pWait->pvWaitChan);
+}
+
+/**
+ * Do the actual wait.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemBsdWaitDoIt(PRTR0SEMBSDSLEEP pWait)
+{
+ int rcBsd;
+ int fSleepqFlags = SLEEPQ_CONDVAR;
+
+ if (pWait->fInterruptible)
+ fSleepqFlags |= SLEEPQ_INTERRUPTIBLE;
+
+ sleepq_add(pWait->pvWaitChan, NULL, "VBoxIS", fSleepqFlags, 0);
+
+ if (!pWait->fIndefinite)
+ {
+ sleepq_set_timeout(pWait->pvWaitChan, pWait->iTimeout);
+
+ if (pWait->fInterruptible)
+ rcBsd = SLEEPQ_TIMEDWAIT_SIG(pWait->pvWaitChan);
+ else
+ rcBsd = SLEEPQ_TIMEDWAIT(pWait->pvWaitChan);
+ }
+ else
+ {
+ if (pWait->fInterruptible)
+ rcBsd = SLEEPQ_WAIT_SIG(pWait->pvWaitChan);
+ else
+ {
+ rcBsd = 0;
+ SLEEPQ_WAIT(pWait->pvWaitChan);
+ }
+ }
+
+ switch (rcBsd)
+ {
+ case 0:
+ break;
+ case ERESTART:
+ {
+ if (!pWait->fIndefinite)
+ {
+ /* Recalc timeout. */
+ uint64_t u64Now = RTTimeSystemNanoTS();
+ if (u64Now >= pWait->uNsAbsTimeout)
+ pWait->fTimedOut = true;
+ else
+ {
+ u64Now = pWait->uNsAbsTimeout - u64Now;
+ rtR0SemBsdWaitUpdateTimeout(pWait, u64Now);
+ }
+ }
+ break;
+ }
+ case EWOULDBLOCK:
+ pWait->fTimedOut = true;
+ break;
+ case EINTR:
+ Assert(pWait->fInterruptible);
+ pWait->fInterrupted = true;
+ break;
+ default:
+ AssertMsgFailed(("sleepq_* -> %d\n", rcBsd));
+ break;
+ }
+}
+
+
+/**
+ * Checks if a FreeBSD wait was interrupted.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ * @remarks This shall be called before the first rtR0SemBsdWaitDoIt().
+ */
+DECLINLINE(bool) rtR0SemBsdWaitWasInterrupted(PRTR0SEMBSDSLEEP pWait)
+{
+ return pWait->fInterrupted;
+}
+
+
+/**
+ * Checks if a FreeBSD wait has timed out.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ */
+DECLINLINE(bool) rtR0SemBsdWaitHasTimedOut(PRTR0SEMBSDSLEEP pWait)
+{
+ return pWait->fTimedOut;
+}
+
+
+/**
+ * Deletes a FreeBSD wait.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemBsdWaitDelete(PRTR0SEMBSDSLEEP pWait)
+{
+ sleepq_release(pWait->pvWaitChan);
+}
+
+
+/**
+ * Signals the wait channel.
+ *
+ * @param pvWaitChan The opaque wait channel handle.
+ */
+DECLINLINE(void) rtR0SemBsdSignal(void *pvWaitChan)
+{
+ sleepq_lock(pvWaitChan);
+ int fWakeupSwapProc = sleepq_signal(pvWaitChan, SLEEPQ_CONDVAR, 0, 0);
+ sleepq_release(pvWaitChan);
+ if (fWakeupSwapProc)
+ kick_proc0();
+}
+
+/**
+ * Wakes up all waiters on the wait channel.
+ *
+ * @param pvWaitChan The opaque wait channel handle.
+ */
+DECLINLINE(void) rtR0SemBsdBroadcast(void *pvWaitChan)
+{
+ sleepq_lock(pvWaitChan);
+ sleepq_broadcast(pvWaitChan, SLEEPQ_CONDVAR, 0, 0);
+#if __FreeBSD_version >= 800000 /* Broadcast releases the sleep queue lock on FreeBSD 7.x */
+ sleepq_release(pvWaitChan);
+#endif
+}
+
+/**
+ * Gets the max resolution of the timeout machinery.
+ *
+ * @returns Resolution specified in nanoseconds.
+ */
+DECLINLINE(uint32_t) rtR0SemBsdWaitGetResolution(void)
+{
+ return 1000000000 / hz; /* ns */
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h */
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c
new file mode 100644
index 00000000..bbf54a88
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c
@@ -0,0 +1,244 @@
+/* $Id: spinlock-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/spinlock.h>
+#include <iprt/errcore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/thread.h>
+#include <iprt/mp.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the struct mtx type.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The spinlock. */
+ uint32_t volatile fLocked;
+ /** Saved interrupt flag. */
+ uint32_t volatile fIntSaved;
+ /** The spinlock creation flags. */
+ uint32_t fFlags;
+#ifdef RT_MORE_STRICT
+ /** The idAssertCpu variable before acquring the lock for asserting after
+ * releasing the spinlock. */
+ RTCPUID volatile idAssertCpu;
+ /** The CPU that owns the lock. */
+ RTCPUID volatile idCpuOwner;
+#endif
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate.
+ */
+ AssertCompile(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize & return.
+ */
+ pThis->u32Magic = RTSPINLOCK_MAGIC;
+ pThis->fLocked = 0;
+ pThis->fFlags = fFlags;
+ pThis->fIntSaved = 0;
+
+ *pSpinlock = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ RT_ASSERT_INTS_ON();
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertMsgReturn(pThis->u32Magic == RTSPINLOCK_MAGIC,
+ ("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Make the lock invalid and release the memory.
+ */
+ ASMAtomicIncU32(&pThis->u32Magic);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+ for (;;)
+ {
+ uint32_t fIntSaved = ASMIntDisableFlags();
+ critical_enter();
+
+ int c = 50;
+ for (;;)
+ {
+ if (ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0))
+ {
+ RT_ASSERT_PREEMPT_CPUID_SPIN_ACQUIRED(pThis);
+ pThis->fIntSaved = fIntSaved;
+ return;
+ }
+ if (--c <= 0)
+ break;
+ cpu_spinwait();
+ }
+
+ /* Enable interrupts while we sleep. */
+ ASMSetFlags(fIntSaved);
+ critical_exit();
+ DELAY(1);
+ }
+ }
+ else
+ {
+ for (;;)
+ {
+ critical_enter();
+
+ int c = 50;
+ for (;;)
+ {
+ if (ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0))
+ {
+ RT_ASSERT_PREEMPT_CPUID_SPIN_ACQUIRED(pThis);
+ return;
+ }
+ if (--c <= 0)
+ break;
+ cpu_spinwait();
+ }
+
+ critical_exit();
+ DELAY(1);
+ }
+ }
+}
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE_VARS();
+
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+ RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE(pThis);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+ uint32_t fIntSaved = pThis->fIntSaved;
+ pThis->fIntSaved = 0;
+ if (ASMAtomicCmpXchgU32(&pThis->fLocked, 0, 1))
+ ASMSetFlags(fIntSaved);
+ else
+ AssertMsgFailed(("Spinlock %p was not locked!\n", pThis));
+ }
+ else
+ {
+ if (!ASMAtomicCmpXchgU32(&pThis->fLocked, 0, 1))
+ AssertMsgFailed(("Spinlock %p was not locked!\n", pThis));
+ }
+
+ critical_exit();
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h b/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h
new file mode 100644
index 00000000..d5f959c7
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h
@@ -0,0 +1,170 @@
+/* $Id: the-freebsd-kernel.h $ */
+/** @file
+ * IPRT - Ring-0 Driver, The FreeBSD Kernel Headers.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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.
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r0drv_freebsd_the_freebsd_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_freebsd_the_freebsd_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/* Deal with conflicts first. */
+#include <sys/param.h>
+#undef PVM
+#include <sys/bus.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/uio.h>
+#include <sys/libkern.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/limits.h>
+#include <sys/unistd.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#if __FreeBSD_version >= 1000030
+#include <sys/rwlock.h>
+#endif
+#include <sys/mutex.h>
+#include <sys/sched.h>
+#include <sys/callout.h>
+#include <sys/cpu.h>
+#include <sys/smp.h>
+#include <sys/sleepqueue.h>
+#include <sys/sx.h>
+#include <vm/vm.h>
+#include <vm/pmap.h> /* for vtophys */
+#include <vm/vm_map.h>
+#include <vm/vm_object.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_param.h> /* KERN_SUCCESS ++ */
+#include <vm/vm_page.h>
+#include <vm/vm_phys.h> /* vm_phys_alloc_* */
+#include <vm/vm_extern.h> /* kmem_alloc_attr */
+#include <vm/vm_pageout.h> /* vm_contig_grow_cache */
+#include <sys/vmmeter.h> /* cnt */
+#include <sys/resourcevar.h>
+#include <machine/cpu.h>
+
+/**
+ * Wrappers around the sleepq_ KPI.
+ */
+#if __FreeBSD_version >= 800026
+# define SLEEPQ_TIMEDWAIT(EventInt) sleepq_timedwait(EventInt, 0)
+# define SLEEPQ_TIMEDWAIT_SIG(EventInt) sleepq_timedwait_sig(EventInt, 0)
+# define SLEEPQ_WAIT(EventInt) sleepq_wait(EventInt, 0)
+# define SLEEPQ_WAIT_SIG(EventInt) sleepq_wait_sig(EventInt, 0)
+#else
+# define SLEEPQ_TIMEDWAIT(EventInt) sleepq_timedwait(EventInt)
+# define SLEEPQ_TIMEDWAIT_SIG(EventInt) sleepq_timedwait_sig(EventInt)
+# define SLEEPQ_WAIT(EventInt) sleepq_wait(EventInt)
+# define SLEEPQ_WAIT_SIG(EventInt) sleepq_wait_sig(EventInt)
+#endif
+
+/**
+ * Our pmap_enter version
+ */
+#if __FreeBSD_version >= 701105
+# define MY_PMAP_ENTER(pPhysMap, AddrR3, pPage, fProt, fWired) \
+ pmap_enter(pPhysMap, AddrR3, VM_PROT_NONE, pPage, fProt, fWired)
+#else
+# define MY_PMAP_ENTER(pPhysMap, AddrR3, pPage, fProt, fWired) \
+ pmap_enter(pPhysMap, AddrR3, pPage, fProt, fWired)
+#endif
+
+/**
+ * The VM object lock/unlock wrappers for older kernels.
+ */
+#if __FreeBSD_version < 1000030
+# define VM_OBJECT_WLOCK(a_pObject) VM_OBJECT_LOCK((a_pObject))
+# define VM_OBJECT_WUNLOCK(a_pObject) VM_OBJECT_UNLOCK((a_pObject))
+#endif
+
+#if __FreeBSD_version >= 1100077
+# define MY_LIM_MAX_PROC(a_pProc, a_Limit) lim_max_proc((a_pProc), (a_Limit))
+#else
+# define MY_LIM_MAX_PROC(a_pProc, a_Limit) lim_max((a_pProc), (a_Limit))
+#endif
+
+/**
+ * Check whether we can use kmem_alloc_attr for low allocs.
+ */
+#if (__FreeBSD_version >= 900011) \
+ || (__FreeBSD_version < 900000 && __FreeBSD_version >= 800505) \
+ || (__FreeBSD_version < 800000 && __FreeBSD_version >= 703101)
+# define USE_KMEM_ALLOC_ATTR
+#endif
+
+/**
+ * Check whether we can use kmem_alloc_prot.
+ */
+#if 0 /** @todo Not available yet. */
+# define USE_KMEM_ALLOC_PROT
+#endif
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_freebsd_the_freebsd_kernel_h */
diff --git a/src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c
new file mode 100644
index 00000000..6b4181c9
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c
@@ -0,0 +1,204 @@
+/* $Id: thread-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Threads (Part 1), Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * 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 "the-freebsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+#include "internal/thread.h"
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)curthread;
+}
+
+
+static int rtR0ThreadFbsdSleepCommon(RTMSINTERVAL cMillies)
+{
+ int rc;
+ int cTicks;
+
+ /*
+ * 0 ms sleep -> yield.
+ */
+ if (!cMillies)
+ {
+ RTThreadYield();
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Translate milliseconds into ticks and go to sleep.
+ */
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ if (hz == 1000)
+ cTicks = cMillies;
+ else if (hz == 100)
+ cTicks = cMillies / 10;
+ else
+ {
+ int64_t cTicks64 = ((uint64_t)cMillies * hz) / 1000;
+ cTicks = (int)cTicks64;
+ if (cTicks != cTicks64)
+ cTicks = INT_MAX;
+ }
+ }
+ else
+ cTicks = 0; /* requires giant lock! */
+
+ rc = tsleep((void *)RTThreadSleep,
+ PZERO | PCATCH,
+ "iprtsl", /* max 6 chars */
+ cTicks);
+ switch (rc)
+ {
+ case 0:
+ return VINF_SUCCESS;
+ case EWOULDBLOCK:
+ return VERR_TIMEOUT;
+ case EINTR:
+ case ERESTART:
+ return VERR_INTERRUPTED;
+ default:
+ AssertMsgFailed(("%d\n", rc));
+ return VERR_NO_TRANSLATION;
+ }
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadFbsdSleepCommon(cMillies);
+}
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadFbsdSleepCommon(cMillies);
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+#if __FreeBSD_version >= 900032
+ kern_yield(curthread->td_user_pri);
+#else
+ uio_yield();
+#endif
+ return false; /** @todo figure this one ... */
+}
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+
+ return curthread->td_critnest == 0
+ && ASMIntAreEnabled(); /** @todo is there a native freebsd function/macro for this? */
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+
+ return curthread->td_owepreempt == 1;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* yes, RTThreadPreemptIsPending is reliable. */
+ return true;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+ /* yes, kernel preemption is possible. */
+ return true;
+}
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 0);
+ pState->u32Reserved = 42;
+
+ critical_enter();
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+}
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 42);
+ pState->u32Reserved = 0;
+
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+ critical_exit();
+}
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); NOREF(hThread);
+ /** @todo FreeBSD: Implement RTThreadIsInInterrupt. Required for guest
+ * additions! */
+ return !ASMIntAreEnabled();
+}
+
+
+RTDECL(int) RTThreadQueryTerminationStatus(RTTHREAD hThread)
+{
+ RT_NOREF(hThread);
+ /** @todo implement. */
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c
new file mode 100644
index 00000000..df0613ba
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c
@@ -0,0 +1,189 @@
+/* $Id: thread2-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+#include "internal/thread.h"
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative(RTThreadNativeSelf());
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ int iPriority;
+
+ switch (enmType)
+ {
+ case RTTHREADTYPE_INFREQUENT_POLLER: iPriority = PZERO + 8; break;
+ case RTTHREADTYPE_EMULATION: iPriority = PZERO + 4; break;
+ case RTTHREADTYPE_DEFAULT: iPriority = PZERO; break;
+ case RTTHREADTYPE_MSG_PUMP: iPriority = PZERO - 4; break;
+ case RTTHREADTYPE_IO: iPriority = PRIBIO; break;
+ case RTTHREADTYPE_TIMER: iPriority = PRI_MIN_KERN; break;
+ default:
+ AssertMsgFailed(("enmType=%d\n", enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+
+#if __FreeBSD_version < 700000
+ /* Do like they're doing in subr_ntoskrnl.c... */
+ mtx_lock_spin(&sched_lock);
+#else
+ thread_lock(curthread);
+#endif
+ sched_prio(curthread, iPriority);
+#if __FreeBSD_version < 600000
+ curthread->td_base_pri = iPriority;
+#endif
+#if __FreeBSD_version < 700000
+ mtx_unlock_spin(&sched_lock);
+#else
+ thread_unlock(curthread);
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+ /* There is nothing special that needs doing here, but the
+ user really better know what he's cooking. */
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ /** @todo fix RTThreadWait/RTR0Term race on freebsd. */
+ RTThreadSleep(1);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+/**
+ * Native thread main function.
+ *
+ * @param pvThreadInt The thread structure.
+ */
+static void rtThreadNativeMain(void *pvThreadInt)
+{
+ const struct thread *Self = curthread;
+ PRTTHREADINT pThreadInt = (PRTTHREADINT)pvThreadInt;
+ int rc;
+
+ rc = rtThreadMain(pThreadInt, (RTNATIVETHREAD)Self, &pThreadInt->szName[0]);
+
+#if __FreeBSD_version >= 800002
+ kproc_exit(rc);
+#else
+ kthread_exit(rc);
+#endif
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+ int rc;
+ struct proc *pProc;
+
+#if __FreeBSD_version >= 800002
+ rc = kproc_create(rtThreadNativeMain, pThreadInt, &pProc, RFHIGHPID, 0, "%s", pThreadInt->szName);
+#else
+ rc = kthread_create(rtThreadNativeMain, pThreadInt, &pProc, RFHIGHPID, 0, "%s", pThreadInt->szName);
+#endif
+ if (!rc)
+ {
+ *pNativeThread = (RTNATIVETHREAD)FIRST_THREAD_IN_PROC(pProc);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromErrno(rc);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c
new file mode 100644
index 00000000..ba314eab
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c
@@ -0,0 +1,108 @@
+/* $Id: time-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+#define RTTIME_INCL_TIMESPEC
+
+#include <iprt/time.h>
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ struct timespec tsp;
+ nanouptime(&tsp);
+ return tsp.tv_sec * RT_NS_1SEC_64
+ + tsp.tv_nsec;
+}
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return RTTimeNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return RTTimeNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return RTTimeMilliTS();
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ struct timespec tsp;
+ nanotime(&tsp);
+ return RTTimeSpecSetTimespec(pTime, &tsp);
+}
+
diff --git a/src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c
new file mode 100644
index 00000000..1f92b3d3
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c
@@ -0,0 +1,320 @@
+/* $Id: timer-r0drv-freebsd.c $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, FreeBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-freebsd-kernel.h"
+
+#include <iprt/timer.h>
+#include <iprt/time.h>
+#include <iprt/spinlock.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of an FreeBSD timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Flag indicating that the timer is suspended. */
+ uint8_t volatile fSuspended;
+ /** Whether the timer must run on a specific CPU or not. */
+ uint8_t fSpecificCpu;
+ /** The CPU it must run on if fSpecificCpu is set. */
+ uint32_t iCpu;
+ /** The FreeBSD callout structure. */
+ struct callout Callout;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** User argument. */
+ void *pvUser;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t u64NanoInterval;
+ /** The start of the current run.
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64StartTS;
+ /** The start of the current run.
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64NextTS;
+ /** The current tick number (since u64StartTS). */
+ uint64_t volatile iTick;
+} RTTIMER;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtTimerFreeBSDCallback(void *pvTimer);
+
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ *ppTimer = NULL;
+
+ /*
+ * Validate flags.
+ */
+ if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
+ return VERR_INVALID_PARAMETER;
+ if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
+ && (fFlags & RTTIMER_FLAGS_CPU_MASK) > mp_maxid)
+ return VERR_CPU_NOT_FOUND;
+
+ /*
+ * Allocate and initialize the timer handle.
+ */
+ PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
+ if (!pTimer)
+ return VERR_NO_MEMORY;
+
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->fSuspended = true;
+ pTimer->fSpecificCpu = !!(fFlags & RTTIMER_FLAGS_CPU_SPECIFIC);
+ pTimer->iCpu = fFlags & RTTIMER_FLAGS_CPU_MASK;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->u64NanoInterval = u64NanoInterval;
+ pTimer->u64StartTS = 0;
+ callout_init(&pTimer->Callout, CALLOUT_MPSAFE);
+
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates the timer handle.
+ *
+ * @returns true if valid, false if invalid.
+ * @param pTimer The handle.
+ */
+DECLINLINE(bool) rtTimerIsValid(PRTTIMER pTimer)
+{
+ AssertPtrReturn(pTimer, false);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, false);
+ return true;
+}
+
+
+RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ /* It's ok to pass NULL pointer. */
+ if (pTimer == /*NIL_RTTIMER*/ NULL)
+ return VINF_SUCCESS;
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Free the associated resources.
+ */
+ pTimer->u32Magic++;
+ callout_stop(&pTimer->Callout);
+ RTMemFree(pTimer);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ struct timeval tv;
+
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ if (!pTimer->fSuspended)
+ return VERR_TIMER_ACTIVE;
+ if ( pTimer->fSpecificCpu
+ && !RTMpIsCpuOnline(RTMpCpuIdFromSetIndex(pTimer->iCpu)))
+ return VERR_CPU_OFFLINE;
+
+ /*
+ * Calc when it should start firing.
+ */
+ u64First += RTTimeNanoTS();
+
+ pTimer->fSuspended = false;
+ pTimer->iTick = 0;
+ pTimer->u64StartTS = u64First;
+ pTimer->u64NextTS = u64First;
+
+ tv.tv_sec = u64First / 1000000000;
+ tv.tv_usec = (u64First % 1000000000) / 1000;
+ callout_reset(&pTimer->Callout, tvtohz(&tv), rtTimerFreeBSDCallback, pTimer);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ if (pTimer->fSuspended)
+ return VERR_TIMER_SUSPENDED;
+
+ /*
+ * Suspend the timer.
+ */
+ pTimer->fSuspended = true;
+ callout_stop(&pTimer->Callout);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * smp_rendezvous action callback.
+ *
+ * This will perform the timer callback if we're on the right CPU.
+ *
+ * @param pvTimer The timer.
+ */
+static void rtTimerFreeBSDIpiAction(void *pvTimer)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvTimer;
+ if ( pTimer->iCpu == RTTIMER_FLAGS_CPU_MASK
+ || (u_int)pTimer->iCpu == curcpu)
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, pTimer->iTick);
+}
+
+
+static void rtTimerFreeBSDCallback(void *pvTimer)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvTimer;
+
+ /* calculate and set the next timeout */
+ pTimer->iTick++;
+ if (!pTimer->u64NanoInterval)
+ {
+ pTimer->fSuspended = true;
+ callout_stop(&pTimer->Callout);
+ }
+ else
+ {
+ struct timeval tv;
+ const uint64_t u64NanoTS = RTTimeNanoTS();
+ pTimer->u64NextTS = pTimer->u64StartTS + pTimer->iTick * pTimer->u64NanoInterval;
+ if (pTimer->u64NextTS < u64NanoTS)
+ pTimer->u64NextTS = u64NanoTS + RTTimerGetSystemGranularity() / 2;
+
+ tv.tv_sec = pTimer->u64NextTS / 1000000000;
+ tv.tv_usec = (pTimer->u64NextTS % 1000000000) / 1000;
+ callout_reset(&pTimer->Callout, tvtohz(&tv), rtTimerFreeBSDCallback, pTimer);
+ }
+
+ /* callback */
+ if ( !pTimer->fSpecificCpu
+ || pTimer->iCpu == curcpu)
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, pTimer->iTick);
+ else
+ smp_rendezvous(NULL, rtTimerFreeBSDIpiAction, NULL, pvTimer);
+}
+
+
+RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
+{
+ return 1000000000 / hz; /* ns */
+}
+
+
+RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(bool) RTTimerCanDoHighResolution(void)
+{
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/generic/Makefile.kup b/src/VBox/Runtime/r0drv/generic/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/generic/RTMpIsCpuWorkPending-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTMpIsCpuWorkPending-r0drv-generic.cpp
new file mode 100644
index 00000000..9173455f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTMpIsCpuWorkPending-r0drv-generic.cpp
@@ -0,0 +1,55 @@
+/* $Id: RTMpIsCpuWorkPending-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - RTMpIsCpuWorkPending, Generic.
+ */
+
+/*
+ * 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 <iprt/mp.h>
+#include "internal/iprt.h"
+
+
+/**
+ * Check if there's work (DPCs on Windows) pending on the current CPU.
+ *
+ * @return true if there's pending work on the current CPU, false otherwise.
+ */
+RTDECL(bool) RTMpIsCpuWorkPending(void)
+{
+ return false;
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuWorkPending);
+
diff --git a/src/VBox/Runtime/r0drv/generic/RTMpOn-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTMpOn-r0drv-generic.cpp
new file mode 100644
index 00000000..c42b0566
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTMpOn-r0drv-generic.cpp
@@ -0,0 +1,104 @@
+/* $Id: RTMpOn-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, Generic Stubs.
+ */
+
+/*
+ * 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 <iprt/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ NOREF(pfnWorker);
+ NOREF(pvUser1);
+ NOREF(pvUser2);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTMpOnAll);
+
+
+RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
+{
+ return false;
+}
+RT_EXPORT_SYMBOL(RTMpOnAllIsConcurrentSafe);
+
+
+RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ NOREF(pfnWorker);
+ NOREF(pvUser1);
+ NOREF(pvUser2);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTMpOnOthers);
+
+
+RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ NOREF(idCpu);
+ NOREF(pfnWorker);
+ NOREF(pvUser1);
+ NOREF(pvUser2);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTMpOnSpecific);
+
+
+RTDECL(int) RTMpOnPair(RTCPUID idCpu1, RTCPUID idCpu2, uint32_t fFlags, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ NOREF(idCpu1);
+ NOREF(idCpu2);
+ NOREF(fFlags);
+ NOREF(pfnWorker);
+ NOREF(pvUser1);
+ NOREF(pvUser2);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTMpOnPair);
+
+
+
+RTDECL(bool) RTMpOnPairIsConcurrentExecSupported(void)
+{
+ return false;
+}
+RT_EXPORT_SYMBOL(RTMpOnPairIsConcurrentExecSupported);
+
diff --git a/src/VBox/Runtime/r0drv/generic/RTMpPokeCpu-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTMpPokeCpu-r0drv-generic.cpp
new file mode 100644
index 00000000..44e44bd5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTMpPokeCpu-r0drv-generic.cpp
@@ -0,0 +1,58 @@
+/* $Id: RTMpPokeCpu-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - RTMpPokeCpu, Generic 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 *
+*********************************************************************************************************************************/
+#include <iprt/mp.h>
+#include "internal/iprt.h"
+
+
+static DECLCALLBACK(void) rtMpNtPokeCpuDummy(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ NOREF(idCpu);
+ NOREF(pvUser1);
+ NOREF(pvUser2);
+}
+
+
+RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
+{
+ return RTMpOnSpecific(idCpu, rtMpNtPokeCpuDummy, NULL, NULL);
+}
+RT_EXPORT_SYMBOL(RTMpPokeCpu);
+
diff --git a/src/VBox/Runtime/r0drv/generic/RTThreadPreemptDisable-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptDisable-r0drv-generic.cpp
new file mode 100644
index 00000000..a0f2efd4
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptDisable-r0drv-generic.cpp
@@ -0,0 +1,54 @@
+/* $Id: RTThreadPreemptDisable-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - RTThreadPreemptDisable, Generic ring-0 driver implementation.
+ */
+
+/*
+ * 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 <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 0);
+ pState->u32Reserved = 42;
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptDisable);
+
diff --git a/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsEnabled-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsEnabled-r0drv-generic.cpp
new file mode 100644
index 00000000..7e20e348
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsEnabled-r0drv-generic.cpp
@@ -0,0 +1,53 @@
+/* $Id: RTThreadPreemptIsEnabled-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - RTThreadPreemptIsEnabled, Generic ring-0 driver implementation.
+ */
+
+/*
+ * 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 <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+ return true;
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptIsEnabled);
+
diff --git a/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPending-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPending-r0drv-generic.cpp
new file mode 100644
index 00000000..9031696f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPending-r0drv-generic.cpp
@@ -0,0 +1,53 @@
+/* $Id: RTThreadPreemptIsPending-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - RTThreadPreemptIsPending, Generic ring-0 driver implementation.
+ */
+
+/*
+ * 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 <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+ return false;
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptIsPending);
+
diff --git a/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPendingTrusty-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPendingTrusty-r0drv-generic.cpp
new file mode 100644
index 00000000..30288f5f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptIsPendingTrusty-r0drv-generic.cpp
@@ -0,0 +1,51 @@
+/* $Id: RTThreadPreemptIsPendingTrusty-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - RTThreadPreemptIsPendingTrusty, Generic ring-0 driver 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 *
+*********************************************************************************************************************************/
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* no, RTThreadPreemptIsPending is not reliable. */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptIsPendingTrusty);
+
diff --git a/src/VBox/Runtime/r0drv/generic/RTThreadPreemptRestore-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptRestore-r0drv-generic.cpp
new file mode 100644
index 00000000..a3923618
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/RTThreadPreemptRestore-r0drv-generic.cpp
@@ -0,0 +1,54 @@
+/* $Id: RTThreadPreemptRestore-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - RTThreadPreemptRestore, Generic ring-0 driver implementation.
+ */
+
+/*
+ * 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 <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 42);
+ pState->u32Reserved = 0;
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptRestore);
+
diff --git a/src/VBox/Runtime/r0drv/generic/mpnotification-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/mpnotification-r0drv-generic.cpp
new file mode 100644
index 00000000..4d7fce5e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/mpnotification-r0drv-generic.cpp
@@ -0,0 +1,75 @@
+/* $Id: mpnotification-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - Multiprocessor Notifications, Ring-0 Driver, Generic Stubs.
+ */
+
+/*
+ * 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 <iprt/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include "r0drv/mp-r0drv.h"
+
+
+RTDECL(int) RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
+{
+ NOREF(pfnCallback);
+ NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpNotificationRegister);
+
+
+RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
+{
+ NOREF(pfnCallback);
+ NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpNotificationDeregister);
+
+
+DECLHIDDEN(int) rtR0MpNotificationInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0MpNotificationTerm(void)
+{
+}
+
diff --git a/src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c b/src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c
new file mode 100644
index 00000000..783c0731
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/semspinmutex-r0drv-generic.c
@@ -0,0 +1,513 @@
+/* $Id: semspinmutex-r0drv-generic.c $ */
+/** @file
+ * IPRT - Spinning Mutex Semaphores, Ring-0 Driver, Generic.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#ifdef RT_OS_WINDOWS
+# include "../nt/the-nt-kernel.h"
+#endif
+#include "internal/iprt.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Saved state information.
+ */
+typedef struct RTSEMSPINMUTEXSTATE
+{
+ /** Saved flags register. */
+ RTCCUINTREG fSavedFlags;
+ /** Preemption state. */
+ RTTHREADPREEMPTSTATE PreemptState;
+ /** Whether to spin or sleep. */
+ bool fSpin;
+ /** Whether the flags have been saved. */
+ bool fValidFlags;
+} RTSEMSPINMUTEXSTATE;
+
+/**
+ * Spinning mutex semaphore.
+ */
+typedef struct RTSEMSPINMUTEXINTERNAL
+{
+ /** Magic value (RTSEMSPINMUTEX_MAGIC)
+ * RTCRITSECT_MAGIC is the value of an initialized & operational section. */
+ uint32_t volatile u32Magic;
+ /** Flags. This is a combination of RTSEMSPINMUTEX_FLAGS_XXX and
+ * RTSEMSPINMUTEX_INT_FLAGS_XXX. */
+ uint32_t volatile fFlags;
+ /** The owner thread.
+ * This is NIL if the semaphore is not owned by anyone. */
+ RTNATIVETHREAD volatile hOwner;
+ /** Number of threads that are fighting for the lock. */
+ int32_t volatile cLockers;
+ /** The semaphore to block on. */
+ RTSEMEVENT hEventSem;
+ /** Saved state information of the owner.
+ * This will be restored by RTSemSpinRelease. */
+ RTSEMSPINMUTEXSTATE SavedState;
+} RTSEMSPINMUTEXINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*#define RTSEMSPINMUTEX_INT_FLAGS_MUST*/
+
+/** Validates the handle, returning if invalid. */
+#define RTSEMSPINMUTEX_VALIDATE_RETURN(pThis) \
+ do \
+ { \
+ uint32_t u32Magic; \
+ AssertPtr(pThis); \
+ u32Magic = (pThis)->u32Magic; \
+ if (u32Magic != RTSEMSPINMUTEX_MAGIC) \
+ { \
+ AssertMsgFailed(("u32Magic=%#x pThis=%p\n", u32Magic, pThis)); \
+ return u32Magic == RTSEMSPINMUTEX_MAGIC_DEAD ? VERR_SEM_DESTROYED : VERR_INVALID_HANDLE; \
+ } \
+ } while (0)
+
+
+RTDECL(int) RTSemSpinMutexCreate(PRTSEMSPINMUTEX phSpinMtx, uint32_t fFlags)
+{
+ RTSEMSPINMUTEXINTERNAL *pThis;
+ int rc;
+
+ AssertReturn(!(fFlags & ~RTSEMSPINMUTEX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtr(phSpinMtx);
+
+ /*
+ * Allocate and initialize the structure.
+ */
+ pThis = (RTSEMSPINMUTEXINTERNAL *)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ pThis->u32Magic = RTSEMSPINMUTEX_MAGIC;
+ pThis->fFlags = fFlags;
+ pThis->hOwner = NIL_RTNATIVETHREAD;
+ pThis->cLockers = 0;
+ rc = RTSemEventCreateEx(&pThis->hEventSem, RTSEMEVENT_FLAGS_NO_LOCK_VAL, NIL_RTLOCKVALCLASS, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ *phSpinMtx = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexCreate);
+
+
+/**
+ * Helper for RTSemSpinMutexTryRequest and RTSemSpinMutexRequest.
+ *
+ * This will check the current context and see if it's usui
+ *
+ * @returns VINF_SUCCESS or VERR_SEM_BAD_CONTEXT.
+ * @param pState Output structure.
+ */
+static int rtSemSpinMutexEnter(RTSEMSPINMUTEXSTATE *pState, RTSEMSPINMUTEXINTERNAL *pThis)
+{
+#ifndef RT_OS_WINDOWS
+ RTTHREADPREEMPTSTATE const StateInit = RTTHREADPREEMPTSTATE_INITIALIZER;
+#endif
+ int rc = VINF_SUCCESS;
+
+ /** @todo Later #1: When entering in interrupt context and we're not able to
+ * wake up threads from it, we could try switch the lock into pure
+ * spinlock mode. This would require that there are no other threads
+ * currently waiting on it and that the RTSEMSPINMUTEX_FLAGS_IRQ_SAFE
+ * flag is set.
+ *
+ * Later #2: Similarly, it is possible to turn on the
+ * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE at run time if we manage to grab the
+ * semaphore ownership at interrupt time. We might want to try delay the
+ * RTSEMSPINMUTEX_FLAGS_IRQ_SAFE even, since we're fine if we get it...
+ */
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * NT: IRQL <= DISPATCH_LEVEL for waking up threads; IRQL < DISPATCH_LEVEL for sleeping.
+ */
+ pState->PreemptState.uchOldIrql = KeGetCurrentIrql();
+ if (pState->PreemptState.uchOldIrql > DISPATCH_LEVEL)
+ return VERR_SEM_BAD_CONTEXT;
+
+ if (pState->PreemptState.uchOldIrql >= DISPATCH_LEVEL)
+ pState->fSpin = true;
+ else
+ {
+ pState->fSpin = false;
+ KeRaiseIrql(DISPATCH_LEVEL, &pState->PreemptState.uchOldIrql);
+ Assert(pState->PreemptState.uchOldIrql < DISPATCH_LEVEL);
+ }
+
+#elif defined(RT_OS_SOLARIS)
+ /*
+ * Solaris: RTSemEventSignal will do bad stuff on S10 if interrupts are disabled.
+ */
+ if (!ASMIntAreEnabled())
+ return VERR_SEM_BAD_CONTEXT;
+
+ pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
+ if (RTThreadIsInInterrupt(NIL_RTTHREAD))
+ {
+ if (!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE))
+ rc = VINF_SEM_BAD_CONTEXT; /* Try, but owner might be interrupted. */
+ pState->fSpin = true;
+ }
+ pState->PreemptState = StateInit;
+ RTThreadPreemptDisable(&pState->PreemptState);
+
+#elif defined(RT_OS_LINUX) || defined(RT_OS_OS2)
+ /*
+ * OSes on which RTSemEventSignal can be called from any context.
+ */
+ pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
+ if (RTThreadIsInInterrupt(NIL_RTTHREAD))
+ {
+ if (!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE))
+ rc = VINF_SEM_BAD_CONTEXT; /* Try, but owner might be interrupted. */
+ pState->fSpin = true;
+ }
+ pState->PreemptState = StateInit;
+ RTThreadPreemptDisable(&pState->PreemptState);
+
+#else /* PORTME: Check for context where we cannot wake up threads. */
+ /*
+ * Default: ASSUME thread can be woken up if interrupts are enabled and
+ * we're not in an interrupt context.
+ * ASSUME that we can go to sleep if preemption is enabled.
+ */
+ if ( RTThreadIsInInterrupt(NIL_RTTHREAD)
+ || !ASMIntAreEnabled())
+ return VERR_SEM_BAD_CONTEXT;
+
+ pState->fSpin = !RTThreadPreemptIsEnabled(NIL_RTTHREAD);
+ pState->PreemptState = StateInit;
+ RTThreadPreemptDisable(&pState->PreemptState);
+#endif
+
+ /*
+ * Disable interrupts if necessary.
+ */
+ pState->fValidFlags = !!(pThis->fFlags & RTSEMSPINMUTEX_FLAGS_IRQ_SAFE);
+ if (pState->fValidFlags)
+ pState->fSavedFlags = ASMIntDisableFlags();
+ else
+ pState->fSavedFlags = 0;
+
+ return rc;
+}
+
+
+/**
+ * Helper for RTSemSpinMutexTryRequest, RTSemSpinMutexRequest and
+ * RTSemSpinMutexRelease.
+ *
+ * @param pState
+ */
+DECL_FORCE_INLINE(void) rtSemSpinMutexLeave(RTSEMSPINMUTEXSTATE *pState)
+{
+ /*
+ * Restore the interrupt flag.
+ */
+ if (pState->fValidFlags)
+ ASMSetFlags(pState->fSavedFlags);
+
+#ifdef RT_OS_WINDOWS
+ /*
+ * NT: Lower the IRQL if we raised it.
+ */
+ if (pState->PreemptState.uchOldIrql < DISPATCH_LEVEL)
+ KeLowerIrql(pState->PreemptState.uchOldIrql);
+#else
+ /*
+ * Default: Restore preemption.
+ */
+ RTThreadPreemptRestore(&pState->PreemptState);
+#endif
+}
+
+
+RTDECL(int) RTSemSpinMutexTryRequest(RTSEMSPINMUTEX hSpinMtx)
+{
+ RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
+ RTNATIVETHREAD hSelf = RTThreadNativeSelf();
+ RTSEMSPINMUTEXSTATE State;
+ bool fRc;
+ int rc;
+
+ Assert(hSelf != NIL_RTNATIVETHREAD);
+ RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
+
+ /*
+ * Check context, disable preemption and save flags if necessary.
+ */
+ rc = rtSemSpinMutexEnter(&State, pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Try take the ownership.
+ */
+ ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
+ if (!fRc)
+ {
+ /* Busy, too bad. Check for attempts at nested access. */
+ rc = VERR_SEM_BUSY;
+ if (RT_UNLIKELY(pThis->hOwner == hSelf))
+ {
+ AssertMsgFailed(("%p attempt at nested access\n"));
+ rc = VERR_SEM_NESTED;
+ }
+
+ rtSemSpinMutexLeave(&State);
+ return rc;
+ }
+
+ /*
+ * We're the semaphore owner.
+ */
+ ASMAtomicIncS32(&pThis->cLockers);
+ pThis->SavedState = State;
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexTryRequest);
+
+
+RTDECL(int) RTSemSpinMutexRequest(RTSEMSPINMUTEX hSpinMtx)
+{
+ RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
+ RTNATIVETHREAD hSelf = RTThreadNativeSelf();
+ RTSEMSPINMUTEXSTATE State;
+ bool fRc;
+ int rc;
+
+ Assert(hSelf != NIL_RTNATIVETHREAD);
+ RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
+
+ /*
+ * Check context, disable preemption and save flags if necessary.
+ */
+ rc = rtSemSpinMutexEnter(&State, pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Try take the ownership.
+ */
+ ASMAtomicIncS32(&pThis->cLockers);
+ ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
+ if (!fRc)
+ {
+ uint32_t cSpins;
+
+ /*
+ * It's busy. Check if it's an attempt at nested access.
+ */
+ if (RT_UNLIKELY(pThis->hOwner == hSelf))
+ {
+ AssertMsgFailed(("%p attempt at nested access\n"));
+ rtSemSpinMutexLeave(&State);
+ return VERR_SEM_NESTED;
+ }
+
+ /*
+ * Return if we're in interrupt context and the semaphore isn't
+ * configure to be interrupt safe.
+ */
+ if (rc == VINF_SEM_BAD_CONTEXT)
+ {
+ rtSemSpinMutexLeave(&State);
+ return VERR_SEM_BAD_CONTEXT;
+ }
+
+ /*
+ * Ok, we have to wait.
+ */
+ if (State.fSpin)
+ {
+ for (cSpins = 0; ; cSpins++)
+ {
+ ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
+ if (fRc)
+ break;
+ ASMNopPause();
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
+ {
+ rtSemSpinMutexLeave(&State);
+ return VERR_SEM_DESTROYED;
+ }
+
+ /*
+ * "Yield" once in a while. This may lower our IRQL/PIL which
+ * may preempting us, and it will certainly stop the hammering
+ * of hOwner for a little while.
+ */
+ if ((cSpins & 0x7f) == 0x1f)
+ {
+ rtSemSpinMutexLeave(&State);
+ rtSemSpinMutexEnter(&State, pThis);
+ Assert(State.fSpin);
+ }
+ }
+ }
+ else
+ {
+ for (cSpins = 0;; cSpins++)
+ {
+ ASMAtomicCmpXchgHandle(&pThis->hOwner, hSelf, NIL_RTNATIVETHREAD, fRc);
+ if (fRc)
+ break;
+ ASMNopPause();
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMSPINMUTEX_MAGIC))
+ {
+ rtSemSpinMutexLeave(&State);
+ return VERR_SEM_DESTROYED;
+ }
+
+ if ((cSpins & 15) == 15) /* spin a bit before going sleep (again). */
+ {
+ rtSemSpinMutexLeave(&State);
+
+ rc = RTSemEventWait(pThis->hEventSem, RT_INDEFINITE_WAIT);
+ ASMCompilerBarrier();
+ if (RT_SUCCESS(rc))
+ AssertReturn(pThis->u32Magic == RTSEMSPINMUTEX_MAGIC, VERR_SEM_DESTROYED);
+ else if (rc == VERR_INTERRUPTED)
+ AssertRC(rc); /* shouldn't happen */
+ else
+ {
+ AssertRC(rc);
+ return rc;
+ }
+
+ rc = rtSemSpinMutexEnter(&State, pThis);
+ AssertRCReturn(rc, rc);
+ Assert(!State.fSpin);
+ }
+ }
+ }
+ }
+
+ /*
+ * We're the semaphore owner.
+ */
+ pThis->SavedState = State;
+ Assert(pThis->hOwner == hSelf);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexRequest);
+
+
+RTDECL(int) RTSemSpinMutexRelease(RTSEMSPINMUTEX hSpinMtx)
+{
+ RTSEMSPINMUTEXINTERNAL *pThis = hSpinMtx;
+ RTNATIVETHREAD hSelf = RTThreadNativeSelf();
+ uint32_t cLockers;
+ RTSEMSPINMUTEXSTATE State;
+ bool fRc;
+
+ Assert(hSelf != NIL_RTNATIVETHREAD);
+ RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
+
+ /*
+ * Get the saved state and try release the semaphore.
+ */
+ State = pThis->SavedState;
+ ASMCompilerBarrier();
+ ASMAtomicCmpXchgHandle(&pThis->hOwner, NIL_RTNATIVETHREAD, hSelf, fRc);
+ AssertMsgReturn(fRc,
+ ("hOwner=%p hSelf=%p cLockers=%d\n", pThis->hOwner, hSelf, pThis->cLockers),
+ VERR_NOT_OWNER);
+
+ cLockers = ASMAtomicDecS32(&pThis->cLockers);
+ rtSemSpinMutexLeave(&State);
+ if (cLockers > 0)
+ {
+ int rc = RTSemEventSignal(pThis->hEventSem);
+ AssertReleaseMsg(RT_SUCCESS(rc), ("RTSemEventSignal -> %Rrc\n", rc));
+ }
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexRelease);
+
+
+RTDECL(int) RTSemSpinMutexDestroy(RTSEMSPINMUTEX hSpinMtx)
+{
+ RTSEMSPINMUTEXINTERNAL *pThis;
+ RTSEMEVENT hEventSem;
+ int rc;
+
+ if (hSpinMtx == NIL_RTSEMSPINMUTEX)
+ return VINF_SUCCESS;
+ pThis = hSpinMtx;
+ RTSEMSPINMUTEX_VALIDATE_RETURN(pThis);
+
+ /* No destruction races allowed! */
+ AssertMsg( pThis->cLockers == 0
+ && pThis->hOwner == NIL_RTNATIVETHREAD,
+ ("pThis=%p cLockers=%d hOwner=%p\n", pThis, pThis->cLockers, pThis->hOwner));
+
+ /*
+ * Invalidate the structure, free the mutex and free the structure.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMSPINMUTEX_MAGIC_DEAD);
+ hEventSem = pThis->hEventSem;
+ pThis->hEventSem = NIL_RTSEMEVENT;
+ rc = RTSemEventDestroy(hEventSem); AssertRC(rc);
+
+ RTMemFree(pThis);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemSpinMutexDestroy);
+
diff --git a/src/VBox/Runtime/r0drv/generic/threadctxhooks-r0drv-generic.cpp b/src/VBox/Runtime/r0drv/generic/threadctxhooks-r0drv-generic.cpp
new file mode 100644
index 00000000..4291aef1
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/generic/threadctxhooks-r0drv-generic.cpp
@@ -0,0 +1,83 @@
+/* $Id: threadctxhooks-r0drv-generic.cpp $ */
+/** @file
+ * IPRT - Thread Context Switching Hook, Ring-0 Driver, Generic.
+ */
+
+/*
+ * Copyright (C) 2013-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/thread.h>
+#include <iprt/errcore.h>
+
+#include "internal/iprt.h"
+
+RTDECL(int) RTThreadCtxHookCreate(PRTTHREADCTXHOOK phCtxHook, uint32_t fFlags, PFNRTTHREADCTXHOOK pfnCallback, void *pvUser)
+{
+ RT_NOREF4(phCtxHook, fFlags, pfnCallback, pvUser);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookCreate);
+
+
+RTDECL(int) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
+{
+ return hCtxHook == NIL_RTTHREADCTXHOOK ? VINF_SUCCESS : VERR_INVALID_HANDLE;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookDestroy);
+
+
+RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
+{
+ NOREF(hCtxHook);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookEnable);
+
+
+RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
+{
+ NOREF(hCtxHook);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookDisable);
+
+
+RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
+{
+ NOREF(hCtxHook);
+ return false;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookIsEnabled);
+
diff --git a/src/VBox/Runtime/r0drv/haiku/Makefile.kup b/src/VBox/Runtime/r0drv/haiku/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/haiku/RTLogWriteDebugger-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/RTLogWriteDebugger-r0drv-haiku.c
new file mode 100644
index 00000000..849d9025
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/RTLogWriteDebugger-r0drv-haiku.c
@@ -0,0 +1,52 @@
+/* $Id: RTLogWriteDebugger-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Log To Debugger, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/log.h>
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ /** @todo implement this */
+ /*kprintf("%.*s", (int)cb, pch);*/
+ return;
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/RTLogWriteStdOut-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/RTLogWriteStdOut-r0drv-haiku.c
new file mode 100644
index 00000000..7b619a2c
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/RTLogWriteStdOut-r0drv-haiku.c
@@ -0,0 +1,51 @@
+/* $Id: RTLogWriteStdOut-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Log To StdOut, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/log.h>
+
+
+RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb)
+{
+ dprintf("%.*s", (int)cb, pch);
+ return;
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/alloc-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/alloc-r0drv-haiku.c
new file mode 100644
index 00000000..d175cae2
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/alloc-r0drv-haiku.c
@@ -0,0 +1,134 @@
+/* $Id: alloc-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+#include <iprt/log.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/thread.h>
+#include "r0drv/alloc-r0drv.h"
+
+
+/**
+ * OS specific allocation function.
+ */
+int rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ if (RT_UNLIKELY(fFlags & RTMEMHDR_FLAG_ANY_CTX))
+ return VERR_NOT_SUPPORTED;
+
+ PRTMEMHDR pHdr = (PRTMEMHDR)malloc(cb + sizeof(*pHdr));
+ if (RT_UNLIKELY(!pHdr))
+ {
+ LogRel(("rtR0MemAllocEx(%u, %#x) failed\n",(unsigned)cb + sizeof(*pHdr), fFlags));
+ return VERR_NO_MEMORY;
+ }
+
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = cb;
+ pHdr->cbReq = cb;
+ *ppHdr = pHdr;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * OS specific free function.
+ */
+void rtR0MemFree(PRTMEMHDR pHdr)
+{
+ pHdr->u32Magic += 1;
+ free(pHdr);
+}
+
+
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb) RT_NO_THROW_DEF
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(pPhys);
+ Assert(cb > 0);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /*
+ * Allocate the memory and ensure that the API is still providing
+ * memory that's always below 4GB.
+ */
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+ void *pv;
+ area_id area = create_area("VirtualBox Contig Alloc", &pv, B_ANY_KERNEL_ADDRESS, cb, B_32_BIT_CONTIGUOUS,
+ B_READ_AREA | B_WRITE_AREA);
+ if (area >= 0)
+ {
+ physical_entry physMap[2];
+ if (get_memory_map(pv, cb, physMap, 2)>= B_OK)
+ {
+ *pPhys = physMap[0].address;
+ return pv;
+ }
+ delete_area(area);
+ AssertMsgFailed(("Cannot get_memory_map for contig alloc! cb=%u\n",(unsigned)cb));
+ }
+ else
+ AssertMsgFailed(("Cannot create_area for contig alloc! cb=%u error=0x%08lx\n",(unsigned)cb, area));
+ return NULL;
+}
+
+
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ RT_ASSERT_PREEMPTIBLE();
+ if (pv)
+ {
+ Assert(cb > 0);
+
+ area_id area = area_for(pv);
+ if (area >= B_OK)
+ delete_area(area);
+ else
+ AssertMsgFailed(("Cannot find area to delete! cb=%u error=0x%08lx\n",(unsigned)cb, area));
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/assert-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/assert-r0drv-haiku.c
new file mode 100644
index 00000000..7a205945
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/assert-r0drv-haiku.c
@@ -0,0 +1,78 @@
+/* $Id: assert-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/assert.h>
+
+#include <iprt/asm.h>
+#include <iprt/log.h>
+#include <iprt/stdarg.h>
+#include <iprt/string.h>
+
+#include "internal/assert.h"
+
+
+void rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ dprintf("\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%d) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+void rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+ char szMsg[256];
+
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+ dprintf("%s", szMsg);
+
+ NOREF(fInitial);
+}
+
+
+RTR0DECL(void) RTR0AssertPanicSystem(void)
+{
+ panic("%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/initterm-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/initterm-r0drv-haiku.c
new file mode 100644
index 00000000..805d7836
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/initterm-r0drv-haiku.c
@@ -0,0 +1,58 @@
+/* $Id: initterm-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Initialization & Termination, R0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include "internal/initterm.h"
+
+
+int rtR0InitNative(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+void rtR0TermNative(void)
+{
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/memobj-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/memobj-r0drv-haiku.c
new file mode 100644
index 00000000..399ea92f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/memobj-r0drv-haiku.c
@@ -0,0 +1,686 @@
+/* $Id: memobj-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+
+#include <iprt/memobj.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include "internal/memobj.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The Haiku version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJHAIKU
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ /** Area identifier */
+ area_id AreaId;
+} RTR0MEMOBJHAIKU, *PRTR0MEMOBJHAIKU;
+
+
+//MALLOC_DEFINE(M_IPRTMOBJ, "iprtmobj", "IPRT - R0MemObj");
+#if 0
+/**
+ * Gets the virtual memory map the specified object is mapped into.
+ *
+ * @returns VM map handle on success, NULL if no map.
+ * @param pMem The memory object.
+ */
+static vm_map_t rtR0MemObjHaikuGetMap(PRTR0MEMOBJINTERNAL pMem)
+{
+ switch (pMem->enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ return kernel_map;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ return NULL; /* pretend these have no mapping atm. */
+
+ case RTR0MEMOBJTYPE_LOCK:
+ return pMem->u.Lock.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.Lock.R0Process)->p_vmspace->vm_map;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ return pMem->u.ResVirt.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.ResVirt.R0Process)->p_vmspace->vm_map;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ return pMem->u.Mapping.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.Mapping.R0Process)->p_vmspace->vm_map;
+
+ default:
+ return NULL;
+ }
+}
+#endif
+
+
+int rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ PRTR0MEMOBJHAIKU pMemHaiku = (PRTR0MEMOBJHAIKU)pMem;
+ int rc = B_OK;
+
+ switch (pMemHaiku->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ case RTR0MEMOBJTYPE_MAPPING:
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ {
+ if (pMemHaiku->AreaId > -1)
+ rc = delete_area(pMemHaiku->AreaId);
+
+ AssertMsg(rc == B_OK, ("%#x", rc));
+ break;
+ }
+
+ case RTR0MEMOBJTYPE_LOCK:
+ {
+ team_id team = B_SYSTEM_TEAM;
+
+ if (pMemHaiku->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ team = ((team_id)pMemHaiku->Core.u.Lock.R0Process);
+
+ rc = unlock_memory_etc(team, pMemHaiku->Core.pv, pMemHaiku->Core.cb, B_READ_DEVICE);
+ AssertMsg(rc == B_OK, ("%#x", rc));
+ break;
+ }
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ {
+ team_id team = B_SYSTEM_TEAM;
+ if (pMemHaiku->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ team = ((team_id)pMemHaiku->Core.u.Lock.R0Process);
+
+ rc = vm_unreserve_address_range(team, pMemHaiku->Core.pv, pMemHaiku->Core.cb);
+ AssertMsg(rc == B_OK, ("%#x", rc));
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemHaiku->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int rtR0MemObjNativeAllocArea(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, RTR0MEMOBJTYPE enmType,
+ RTHCPHYS PhysHighest, size_t uAlignment, const char *pszTag)
+{
+ NOREF(fExecutable);
+
+ int rc;
+ void *pvMap = NULL;
+ const char *pszName = NULL;
+ uint32 addressSpec = B_ANY_KERNEL_ADDRESS;
+ uint32 fLock = ~0U;
+ LogFlowFunc(("ppMem=%p cb=%u, fExecutable=%s, enmType=%08x, PhysHighest=%RX64 uAlignment=%u\n", ppMem,(unsigned)cb,
+ fExecutable ? "true" : "false", enmType, PhysHighest,(unsigned)uAlignment));
+
+ switch (enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ pszName = "IPRT R0MemObj Alloc";
+ fLock = B_FULL_LOCK;
+ break;
+ case RTR0MEMOBJTYPE_LOW:
+ pszName = "IPRT R0MemObj AllocLow";
+ fLock = B_32_BIT_FULL_LOCK;
+ break;
+ case RTR0MEMOBJTYPE_CONT:
+ pszName = "IPRT R0MemObj AllocCont";
+ fLock = B_32_BIT_CONTIGUOUS;
+ break;
+#if 0
+ case RTR0MEMOBJTYPE_MAPPING:
+ pszName = "IPRT R0MemObj Mapping";
+ fLock = B_FULL_LOCK;
+ break;
+#endif
+ case RTR0MEMOBJTYPE_PHYS:
+ /** @todo alignment */
+ if (uAlignment != PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+ /** @todo r=ramshankar: no 'break' here?? */
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ pszName = "IPRT R0MemObj AllocPhys";
+ fLock = (PhysHighest < _4G ? B_LOMEM : B_32_BIT_CONTIGUOUS);
+ break;
+#if 0
+ case RTR0MEMOBJTYPE_LOCK:
+ break;
+#endif
+ default:
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /* Create the object. */
+ PRTR0MEMOBJHAIKU pMemHaiku;
+ pMemHaiku = (PRTR0MEMOBJHAIKU)rtR0MemObjNew(sizeof(RTR0MEMOBJHAIKU), enmType, NULL, cb, pszTag);
+ if (RT_UNLIKELY(!pMemHaiku))
+ return VERR_NO_MEMORY;
+
+ rc = pMemHaiku->AreaId = create_area(pszName, &pvMap, addressSpec, cb, fLock, B_READ_AREA | B_WRITE_AREA);
+ if (pMemHaiku->AreaId >= 0)
+ {
+ physical_entry physMap[2];
+ pMemHaiku->Core.pv = pvMap; /* store start address */
+ switch (enmType)
+ {
+ case RTR0MEMOBJTYPE_CONT:
+ rc = get_memory_map(pvMap, cb, physMap, 2);
+ if (rc == B_OK)
+ pMemHaiku->Core.u.Cont.Phys = physMap[0].address;
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ rc = get_memory_map(pvMap, cb, physMap, 2);
+ if (rc == B_OK)
+ {
+ pMemHaiku->Core.u.Phys.PhysBase = physMap[0].address;
+ pMemHaiku->Core.u.Phys.fAllocated = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (rc >= B_OK)
+ {
+ *ppMem = &pMemHaiku->Core;
+ return VINF_SUCCESS;
+ }
+
+ delete_area(pMemHaiku->AreaId);
+ }
+
+ rtR0MemObjDelete(&pMemHaiku->Core);
+ return RTErrConvertFromHaikuKernReturn(rc);
+}
+
+
+int rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ return rtR0MemObjNativeAllocArea(ppMem, cb, fExecutable, RTR0MEMOBJTYPE_PAGE, 0 /* PhysHighest */, 0 /* uAlignment */, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag);
+}
+
+
+int rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ return rtR0MemObjNativeAllocArea(ppMem, cb, fExecutable, RTR0MEMOBJTYPE_LOW, 0 /* PhysHighest */, 0 /* uAlignment */, pszTag);
+}
+
+
+int rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ return rtR0MemObjNativeAllocArea(ppMem, cb, fExecutable, RTR0MEMOBJTYPE_CONT, 0 /* PhysHighest */, 0 /* uAlignment */, pszTag);
+}
+
+int rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment, const char *pszTag)
+{
+ return rtR0MemObjNativeAllocArea(ppMem, cb, false, RTR0MEMOBJTYPE_PHYS, PhysHighest, uAlignment, pszTag);
+}
+
+
+int rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ return rtR0MemObjNativeAllocPhys(ppMem, cb, PhysHighest, PAGE_SIZE, pszTag);
+}
+
+
+int rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy, const char *pszTag)
+{
+ AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE, VERR_NOT_SUPPORTED);
+ LogFlowFunc(("ppMem=%p Phys=%08x cb=%u uCachePolicy=%x\n", ppMem, Phys,(unsigned)cb, uCachePolicy));
+
+ /* Create the object. */
+ PRTR0MEMOBJHAIKU pMemHaiku = (PRTR0MEMOBJHAIKU)rtR0MemObjNew(sizeof(*pMemHaiku), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag);
+ if (!pMemHaiku)
+ return VERR_NO_MEMORY;
+
+ /* There is no allocation here, it needs to be mapped somewhere first. */
+ pMemHaiku->AreaId = -1;
+ pMemHaiku->Core.u.Phys.fAllocated = false;
+ pMemHaiku->Core.u.Phys.PhysBase = Phys;
+ pMemHaiku->Core.u.Phys.uCachePolicy = uCachePolicy;
+ *ppMem = &pMemHaiku->Core;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker locking the memory in either kernel or user maps.
+ *
+ * @returns IPRT status code.
+ * @param ppMem Where to store the allocated memory object.
+ * @param pvStart The starting address.
+ * @param cb The size of the block.
+ * @param fAccess The mapping protection to apply.
+ * @param R0Process The process to map the memory to (use NIL_RTR0PROCESS
+ * for the kernel)
+ * @param fFlags Memory flags (B_READ_DEVICE indicates the memory is
+ * intended to be written from a "device").
+ * @param pszTag Allocation tag used for statistics and such.
+ */
+static int rtR0MemObjNativeLockInMap(PPRTR0MEMOBJINTERNAL ppMem, void *pvStart, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, int fFlags, const char *pszTag)
+{
+ NOREF(fAccess);
+ team_id TeamId = B_SYSTEM_TEAM;
+
+ LogFlowFunc(("ppMem=%p pvStart=%p cb=%u fAccess=%x R0Process=%d fFlags=%x\n", ppMem, pvStart, cb, fAccess, R0Process,
+ fFlags));
+
+ /* Create the object. */
+ PRTR0MEMOBJHAIKU pMemHaiku = (PRTR0MEMOBJHAIKU)rtR0MemObjNew(sizeof(*pMemHaiku), RTR0MEMOBJTYPE_LOCK, pvStart, cb, pszTag);
+ if (RT_UNLIKELY(!pMemHaiku))
+ return VERR_NO_MEMORY;
+
+ if (R0Process != NIL_RTR0PROCESS)
+ TeamId = (team_id)R0Process;
+ int rc = lock_memory_etc(TeamId, pvStart, cb, fFlags);
+ if (rc == B_OK)
+ {
+ pMemHaiku->AreaId = -1;
+ pMemHaiku->Core.u.Lock.R0Process = R0Process;
+ *ppMem = &pMemHaiku->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemHaiku->Core);
+ return RTErrConvertFromHaikuKernReturn(rc);
+}
+
+
+int rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess, RTR0PROCESS R0Process,
+ const char *pszTag)
+{
+ return rtR0MemObjNativeLockInMap(ppMem, (void *)R3Ptr, cb, fAccess, R0Process, B_READ_DEVICE, pszTag);
+}
+
+
+int rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ return rtR0MemObjNativeLockInMap(ppMem, pv, cb, fAccess, NIL_RTR0PROCESS, B_READ_DEVICE, pszTag);
+}
+
+
+#if 0
+/** @todo Reserve address space */
+/**
+ * Worker for the two virtual address space reservers.
+ *
+ * We're leaning on the examples provided by mmap and vm_mmap in vm_mmap.c here.
+ */
+static int rtR0MemObjNativeReserveInMap(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process)
+{
+ int rc;
+ team_id TeamId = B_SYSTEM_TEAM;
+
+ LogFlowFunc(("ppMem=%p pvFixed=%p cb=%u uAlignment=%u R0Process=%d\n", ppMem, pvFixed, (unsigned)cb, uAlignment, R0Process));
+
+ if (R0Process != NIL_RTR0PROCESS)
+ team = (team_id)R0Process;
+
+ /* Check that the specified alignment is supported. */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /* Create the object. */
+ PRTR0MEMOBJHAIKU pMemHaiku = (PRTR0MEMOBJHAIKU)rtR0MemObjNew(sizeof(*pMemHaiku), RTR0MEMOBJTYPE_RES_VIRT, NULL, cb);
+ if (!pMemHaiku)
+ return VERR_NO_MEMORY;
+
+ /* Ask the kernel to reserve the address range. */
+ //XXX: vm_reserve_address_range ?
+ return VERR_NOT_SUPPORTED;
+}
+#endif
+
+
+int rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment, const char *pszTag)
+{
+ RT_NOREF(ppMem, pvFixed, cb, uAlignment, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ RT_NOREF(ppMem, R3PtrFixed, cb, uAlignment, R0Process, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ PRTR0MEMOBJHAIKU pMemToMapHaiku = (PRTR0MEMOBJHAIKU)pMemToMap;
+ PRTR0MEMOBJHAIKU pMemHaiku;
+ area_id area = -1;
+ void *pvMap = pvFixed;
+ uint32 uAddrSpec = B_EXACT_ADDRESS;
+ uint32 fProtect = 0;
+ int rc = VERR_MAP_FAILED;
+ AssertMsgReturn(!offSub && !cbSub, ("%#x %#x\n", offSub, cbSub), VERR_NOT_SUPPORTED);
+ AssertMsgReturn(pvFixed == (void *)-1, ("%p\n", pvFixed), VERR_NOT_SUPPORTED);
+#if 0
+ /** @todo r=ramshankar: Wrong format specifiers, fix later! */
+ dprintf("%s(%p, %p, %p, %d, %x, %u, %u)\n", __FUNCTION__, ppMem, pMemToMap, pvFixed, uAlignment,
+ fProt, offSub, cbSub);
+#endif
+ /* Check that the specified alignment is supported. */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /* We can't map anything to the first page, sorry. */
+ if (pvFixed == 0)
+ return VERR_NOT_SUPPORTED;
+
+ if (fProt & RTMEM_PROT_READ)
+ fProtect |= B_KERNEL_READ_AREA;
+ if (fProt & RTMEM_PROT_WRITE)
+ fProtect |= B_KERNEL_WRITE_AREA;
+
+ /*
+ * Either the object we map has an area associated with, which we can clone,
+ * or it's a physical address range which we must map.
+ */
+ if (pMemToMapHaiku->AreaId > -1)
+ {
+ if (pvFixed == (void *)-1)
+ uAddrSpec = B_ANY_KERNEL_ADDRESS;
+
+ rc = area = clone_area("IPRT R0MemObj MapKernel", &pvMap, uAddrSpec, fProtect, pMemToMapHaiku->AreaId);
+ LogFlow(("rtR0MemObjNativeMapKernel: clone_area uAddrSpec=%d fProtect=%x AreaId=%d rc=%d\n", uAddrSpec, fProtect,
+ pMemToMapHaiku->AreaId, rc));
+ }
+ else if (pMemToMapHaiku->Core.enmType == RTR0MEMOBJTYPE_PHYS)
+ {
+ /* map_physical_memory() won't let you choose where. */
+ if (pvFixed != (void *)-1)
+ return VERR_NOT_SUPPORTED;
+ uAddrSpec = B_ANY_KERNEL_ADDRESS;
+
+ rc = area = map_physical_memory("IPRT R0MemObj MapKernelPhys", (phys_addr_t)pMemToMapHaiku->Core.u.Phys.PhysBase,
+ pMemToMapHaiku->Core.cb, uAddrSpec, fProtect, &pvMap);
+ }
+ else
+ return VERR_NOT_SUPPORTED;
+
+ if (rc >= B_OK)
+ {
+ /* Create the object. */
+ pMemHaiku = (PRTR0MEMOBJHAIKU)rtR0MemObjNew(sizeof(RTR0MEMOBJHAIKU), RTR0MEMOBJTYPE_MAPPING, pvMap,
+ pMemToMapHaiku->Core.cb, pszTag);
+ if (RT_UNLIKELY(!pMemHaiku))
+ return VERR_NO_MEMORY;
+
+ pMemHaiku->Core.u.Mapping.R0Process = NIL_RTR0PROCESS;
+ pMemHaiku->Core.pv = pvMap;
+ pMemHaiku->AreaId = area;
+ *ppMem = &pMemHaiku->Core;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_MAP_FAILED;
+
+ /** @todo finish the implementation. */
+
+ rtR0MemObjDelete(&pMemHaiku->Core);
+ return rc;
+}
+
+
+int rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+#if 0
+ /*
+ * Check for unsupported stuff.
+ */
+ AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED);
+ AssertMsgReturn(R3PtrFixed == (RTR3PTR)-1, ("%p\n", R3PtrFixed), VERR_NOT_SUPPORTED);
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+ AssertMsgReturn(!offSub && !cbSub, ("%#zx %#zx\n", offSub, cbSub), VERR_NOT_SUPPORTED); /** @todo implement sub maps */
+
+ int rc;
+ PRTR0MEMOBJHAIKU pMemToMapHaiku = (PRTR0MEMOBJHAIKU)pMemToMap;
+ struct proc *pProc = (struct proc *)R0Process;
+ struct vm_map *pProcMap = &pProc->p_vmspace->vm_map;
+
+ /* calc protection */
+ vm_prot_t ProtectionFlags = 0;
+ if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE)
+ ProtectionFlags = VM_PROT_NONE;
+ if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ)
+ ProtectionFlags |= VM_PROT_READ;
+ if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE)
+ ProtectionFlags |= VM_PROT_WRITE;
+ if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC)
+ ProtectionFlags |= VM_PROT_EXECUTE;
+
+ /* calc mapping address */
+ PROC_LOCK(pProc);
+ vm_offset_t AddrR3 = round_page((vm_offset_t)pProc->p_vmspace->vm_daddr + lim_max(pProc, RLIMIT_DATA));
+ PROC_UNLOCK(pProc);
+
+ /* Insert the object in the map. */
+ rc = vm_map_find(pProcMap, /* Map to insert the object in */
+ NULL, /* Object to map */
+ 0, /* Start offset in the object */
+ &AddrR3, /* Start address IN/OUT */
+ pMemToMap->cb, /* Size of the mapping */
+ TRUE, /* Whether a suitable address should be searched for first */
+ ProtectionFlags, /* protection flags */
+ VM_PROT_ALL, /* Maximum protection flags */
+ 0); /* Copy on write */
+
+ /* Map the memory page by page into the destination map. */
+ if (rc == KERN_SUCCESS)
+ {
+ size_t cPages = pMemToMap->cb >> PAGE_SHIFT;;
+ pmap_t pPhysicalMap = pProcMap->pmap;
+ vm_offset_t AddrR3Dst = AddrR3;
+
+ if ( pMemToMap->enmType == RTR0MEMOBJTYPE_PHYS
+ || pMemToMap->enmType == RTR0MEMOBJTYPE_PHYS_NC
+ || pMemToMap->enmType == RTR0MEMOBJTYPE_PAGE)
+ {
+ /* Mapping physical allocations */
+ Assert(cPages == pMemToMapHaiku->u.Phys.cPages);
+
+ /* Insert the memory page by page into the mapping. */
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ {
+ vm_page_t pPage = pMemToMapHaiku->u.Phys.apPages[iPage];
+
+ MY_PMAP_ENTER(pPhysicalMap, AddrR3Dst, pPage, ProtectionFlags, TRUE);
+ AddrR3Dst += PAGE_SIZE;
+ }
+ }
+ else
+ {
+ /* Mapping cont or low memory types */
+ vm_offset_t AddrToMap = (vm_offset_t)pMemToMap->pv;
+
+ for (uint32_t iPage = 0; iPage < cPages; iPage++)
+ {
+ vm_page_t pPage = PHYS_TO_VM_PAGE(vtophys(AddrToMap));
+
+ MY_PMAP_ENTER(pPhysicalMap, AddrR3Dst, pPage, ProtectionFlags, TRUE);
+ AddrR3Dst += PAGE_SIZE;
+ AddrToMap += PAGE_SIZE;
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a mapping object for it.
+ */
+ PRTR0MEMOBJHAIKU pMemHaiku = (PRTR0MEMOBJHAIKU)rtR0MemObjNew(sizeof(RTR0MEMOBJHAIKU), RTR0MEMOBJTYPE_MAPPING,
+ (void *)AddrR3, pMemToMap->cb, pszTag);
+ if (pMemHaiku)
+ {
+ Assert((vm_offset_t)pMemHaiku->Core.pv == AddrR3);
+ pMemHaiku->Core.u.Mapping.R0Process = R0Process;
+ *ppMem = &pMemHaiku->Core;
+ return VINF_SUCCESS;
+ }
+
+ rc = vm_map_remove(pProcMap, ((vm_offset_t)AddrR3), ((vm_offset_t)AddrR3) + pMemToMap->cb);
+ AssertMsg(rc == KERN_SUCCESS, ("Deleting mapping failed\n"));
+ }
+#else
+ RT_NOREF(ppMem, pMemToMap, R3PtrFixed, uAlignment, fProt, R0Process, offSub, cbSub, pszTag);
+#endif
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTHCPHYS rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ PRTR0MEMOBJHAIKU pMemHaiku = (PRTR0MEMOBJHAIKU)pMem;
+ status_t rc;
+
+ /** @todo r=ramshankar: Validate objects */
+
+ LogFlow(("rtR0MemObjNativeGetPagePhysAddr: pMem=%p enmType=%x iPage=%u\n", pMem, pMemHaiku->Core.enmType,(unsigned)iPage));
+
+ switch (pMemHaiku->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_LOCK:
+ {
+ team_id TeamId = B_SYSTEM_TEAM;
+ physical_entry aPhysMap[2];
+ int32 cPhysMap = 2; /** @todo r=ramshankar: why not use RT_ELEMENTS? */
+
+ if (pMemHaiku->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ TeamId = (team_id)pMemHaiku->Core.u.Lock.R0Process;
+ void *pb = pMemHaiku->Core.pv + (iPage << PAGE_SHIFT);
+
+ rc = get_memory_map_etc(TeamId, pb, B_PAGE_SIZE, aPhysMap, &cPhysMap);
+ if (rc < B_OK || cPhysMap < 1)
+ return NIL_RTHCPHYS;
+
+ return aPhysMap[0].address;
+ }
+
+#if 0
+ case RTR0MEMOBJTYPE_MAPPING:
+ {
+ vm_offset_t pb = (vm_offset_t)pMemHaiku->Core.pv + (iPage << PAGE_SHIFT);
+
+ if (pMemHaiku->Core.u.Mapping.R0Process != NIL_RTR0PROCESS)
+ {
+ struct proc *pProc = (struct proc *)pMemHaiku->Core.u.Mapping.R0Process;
+ struct vm_map *pProcMap = &pProc->p_vmspace->vm_map;
+ pmap_t pPhysicalMap = pProcMap->pmap;
+
+ return pmap_extract(pPhysicalMap, pb);
+ }
+ return vtophys(pb);
+ }
+#endif
+ case RTR0MEMOBJTYPE_CONT:
+ return pMemHaiku->Core.u.Cont.Phys + (iPage << PAGE_SHIFT);
+
+ case RTR0MEMOBJTYPE_PHYS:
+ return pMemHaiku->Core.u.Phys.PhysBase + (iPage << PAGE_SHIFT);
+
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ {
+ team_id TeamId = B_SYSTEM_TEAM;
+ physical_entry aPhysMap[2];
+ int32 cPhysMap = 2; /** @todo r=ramshankar: why not use RT_ELEMENTS? */
+
+ void *pb = pMemHaiku->Core.pv + (iPage << PAGE_SHIFT);
+ rc = get_memory_map_etc(TeamId, pb, B_PAGE_SIZE, aPhysMap, &cPhysMap);
+ if (rc < B_OK || cPhysMap < 1)
+ return NIL_RTHCPHYS;
+
+ return aPhysMap[0].address;
+ }
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ default:
+ return NIL_RTHCPHYS;
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/mp-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/mp-r0drv-haiku.c
new file mode 100644
index 00000000..a9b268e1
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/mp-r0drv-haiku.c
@@ -0,0 +1,246 @@
+/* $Id: mp-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+
+#include <iprt/mp.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/cpuset.h>
+#include "r0drv/mp-r0drv.h"
+
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ return smp_get_current_cpu();
+}
+
+
+RTDECL(int) RTMpCurSetIndex(void)
+{
+ return smp_get_current_cpu();
+}
+
+
+RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu)
+{
+ return *pidCpu = smp_get_current_cpu();
+}
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < smp_get_num_cpus() ? (int)idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < smp_get_num_cpus() ? (RTCPUID)iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return smp_get_num_cpus() - 1;
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return idCpu < smp_get_num_cpus();
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ return smp_get_num_cpus();
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ return idCpu < smp_get_num_cpus();
+ /** @todo FixMe && !CPU_ABSENT(idCpu) */
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ return smp_get_num_cpus();
+}
+
+
+/**
+ * Wrapper between the native Haiku per-cpu callback and PFNRTWORKER
+ * for the RTMpOnAll API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnAllHaikuWrapper(void *pvArg, int current)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ pArgs->pfnWorker(current, pArgs->pvUser1, pArgs->pvUser2);
+}
+
+
+RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RTMPARGS Args;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = NIL_RTCPUID;
+ Args.cHits = 0;
+ /* is _sync needed ? */
+ call_all_cpus_sync(rtmpOnAllHaikuWrapper, &Args);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native Haiku per-cpu callback and PFNRTWORKER
+ * for the RTMpOnOthers API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnOthersHaikuWrapper(void *pvArg, int current)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ RTCPUID idCpu = current;
+ if (pArgs->idCpu != idCpu)
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+}
+
+
+RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ /* Will panic if no rendezvousing cpus, so check up front. */
+ if (RTMpGetOnlineCount() > 1)
+ {
+ RTMPARGS Args;
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = RTMpCpuId();
+ Args.cHits = 0;
+ /* is _sync needed ? */
+ call_all_cpus_sync(rtmpOnOthersHaikuWrapper, &Args);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native Haiku per-cpu callback and PFNRTWORKER
+ * for the RTMpOnSpecific API.
+ *
+ * @param pvArg Pointer to the RTMPARGS package.
+ */
+static void rtmpOnSpecificHaikuWrapper(void *pvArg, int current)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvArg;
+ RTCPUID idCpu = current;
+ if (pArgs->idCpu == idCpu)
+ {
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+ }
+}
+
+
+RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RTMPARGS Args;
+
+ /* Will panic if no rendezvousing cpus, so make sure the cpu is online. */
+ if (!RTMpIsCpuOnline(idCpu))
+ return VERR_CPU_NOT_FOUND;
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu;
+ Args.cHits = 0;
+ /* is _sync needed ? */
+ call_all_cpus_sync(rtmpOnSpecificHaikuWrapper, &Args);
+ return Args.cHits == 1
+ ? VINF_SUCCESS
+ : VERR_CPU_NOT_FOUND;
+}
+
+
+RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
+{
+ return true;
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/process-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/process-r0drv-haiku.c
new file mode 100644
index 00000000..b11d6352
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/process-r0drv-haiku.c
@@ -0,0 +1,56 @@
+/* $Id: process-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Process, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/process.h>
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ return getpid();
+}
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ return (RTR0PROCESS)(team_id)getpid();
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/semevent-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/semevent-r0drv-haiku.c
new file mode 100644
index 00000000..862e1f3c
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/semevent-r0drv-haiku.c
@@ -0,0 +1,282 @@
+/* $Id: semevent-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Single Release Event Semaphores, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Haiku event semaphore.
+ */
+typedef struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The semaphore Id. */
+ sem_id SemId;
+} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+ AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
+
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->SemId = create_sem(0, "IPRT Semaphore Event");
+ if (pThis->SemId >= B_OK)
+ {
+ set_sem_owner(pThis->SemId, B_SYSTEM_TEAM);
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ return VERR_TOO_MANY_SEMAPHORES; /** @todo r=ramshankar: use RTErrConvertFromHaikuKernReturn */
+}
+
+
+/**
+ * Retains a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventHkuRetain(PRTSEMEVENTINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < 100000); NOREF(cRefs);
+}
+
+
+/**
+ * Releases a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventHkuRelease(PRTSEMEVENTINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ RTMemFree(pThis);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and delete the semaphore to unblock everyone.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC);
+ delete_sem(pThis->SemId);
+ pThis->SemId = -1;
+ rtR0SemEventHkuRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ rtR0SemEventHkuRetain(pThis);
+
+ /*
+ * Signal the event object.
+ * We must use B_DO_NOT_RESCHEDULE since we are being used from an irq handler.
+ */
+ release_sem_etc(pThis->SemId, 1, B_DO_NOT_RESCHEDULE);
+ rtR0SemEventHkuRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ status_t status;
+ int rc;
+ int32 flags = 0;
+ bigtime_t timeout; /* in microseconds */
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ timeout = B_INFINITE_TIMEOUT;
+ else
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NANOSECS)
+ timeout = uTimeout / 1000;
+ else if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ timeout = uTimeout * 1000;
+ else
+ return VERR_INVALID_PARAMETER;
+
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ flags |= B_RELATIVE_TIMEOUT;
+ else if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
+ flags |= B_ABSOLUTE_TIMEOUT;
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE)
+ flags |= B_CAN_INTERRUPT;
+ // likely not:
+ //else
+ // flags |= B_KILL_CAN_INTERRUPT;
+
+ rtR0SemEventHkuRetain(pThis);
+
+ status = acquire_sem_etc(pThis->SemId, 1, flags, timeout);
+
+ switch (status)
+ {
+ case B_OK:
+ rc = VINF_SUCCESS;
+ break;
+ case B_BAD_SEM_ID:
+ rc = VERR_SEM_DESTROYED;
+ break;
+ case B_INTERRUPTED:
+ rc = VERR_INTERRUPTED;
+ break;
+ case B_WOULD_BLOCK:
+ /* fallthrough ? */
+ case B_TIMED_OUT:
+ rc = VERR_TIMEOUT;
+ break;
+ default:
+ rc = RTErrConvertFromHaikuKernReturn(status);
+ break;
+ }
+
+ rtR0SemEventHkuRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitEx);
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ /* At least that's what the API supports. */
+ return 1000;
+}
+
+
+RTR0DECL(bool) RTSemEventIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTSemEventIsSignalSafe);
+
diff --git a/src/VBox/Runtime/r0drv/haiku/semeventmulti-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/semeventmulti-r0drv-haiku.c
new file mode 100644
index 00000000..d3b687e7
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/semeventmulti-r0drv-haiku.c
@@ -0,0 +1,310 @@
+/* $Id: semeventmulti-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/lockvalidator.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Haiku multiple release event semaphore.
+ */
+typedef struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The semaphore Id. */
+ sem_id SemId;
+} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /* fFlags */, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis;
+
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->cRefs = 1;
+ pThis->SemId = create_sem(0, "IPRT Semaphore Event Multi");
+ if (pThis->SemId < B_OK)
+ {
+ set_sem_owner(pThis->SemId, B_SYSTEM_TEAM);
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pThis);
+ return VERR_TOO_MANY_SEMAPHORES; /** @todo r=ramshankar: use RTErrConvertFromHaikuKernReturn */
+}
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiHkuRetain(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiHkuRelease(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ {
+ Assert(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC);
+ RTMemFree(pThis);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC);
+ delete_sem(pThis->SemId);
+ pThis->SemId = -1;
+ rtR0SemEventMultiHkuRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiHkuRetain(pThis);
+
+ /*
+ * Signal the event object.
+ * We must use B_DO_NOT_RESCHEDULE since we are being used from an irq handler.
+ */
+ release_sem_etc(pThis->SemId, 1, B_RELEASE_ALL | B_DO_NOT_RESCHEDULE);
+ rtR0SemEventMultiHkuRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiHkuRetain(pThis);
+
+ /*
+ * Reset it.
+ */
+ //FIXME: what should I do ???
+ // delete_sem + create_sem ??
+ rtR0SemEventMultiHkuRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param uTimeout See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventMultiHkuWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ status_t status;
+ int rc;
+ int32 flags = 0;
+ bigtime_t timeout; /* in microseconds */
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ timeout = B_INFINITE_TIMEOUT;
+ else
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NANOSECS)
+ timeout = uTimeout / 1000;
+ else if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ timeout = uTimeout * 1000;
+ else
+ return VERR_INVALID_PARAMETER;
+
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ flags |= B_RELATIVE_TIMEOUT;
+ else if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
+ flags |= B_ABSOLUTE_TIMEOUT;
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE)
+ flags |= B_CAN_INTERRUPT;
+ // likely not:
+ //else
+ // flags |= B_KILL_CAN_INTERRUPT;
+
+ rtR0SemEventMultiHkuRetain(pThis);
+
+ status = acquire_sem_etc(pThis->SemId, 1, flags, timeout);
+
+ switch (status)
+ {
+ case B_OK:
+ rc = VINF_SUCCESS;
+ break;
+ case B_BAD_SEM_ID:
+ rc = VERR_SEM_DESTROYED;
+ break;
+ case B_INTERRUPTED:
+ rc = VERR_INTERRUPTED;
+ break;
+ case B_WOULD_BLOCK:
+ /* fallthrough? */
+ case B_TIMED_OUT:
+ rc = VERR_TIMEOUT;
+ break;
+ default:
+ rc = RTErrConvertFromHaikuKernReturn(status);
+ break;
+ }
+
+ rtR0SemEventMultiHkuRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventMultiHkuWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventMultiHkuWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitEx);
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventMultiHkuWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventMultiGetResolution(void)
+{
+ /* At least that's what the API supports. */
+ return 1000;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiGetResolution);
+
+
+RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiIsSignalSafe);
+
diff --git a/src/VBox/Runtime/r0drv/haiku/semfastmutex-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/semfastmutex-r0drv-haiku.c
new file mode 100644
index 00000000..94065520
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/semfastmutex-r0drv-haiku.c
@@ -0,0 +1,130 @@
+/* $Id: semfastmutex-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/err.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the Haiku (sleep) mutex.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** A good old Benaphore. */
+ vint32 BenId;
+ sem_id SemId;
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ AssertCompile(sizeof(RTSEMFASTMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phFastMtx, VERR_INVALID_POINTER);
+
+ PRTSEMFASTMUTEXINTERNAL pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (RT_UNLIKELY(!pThis))
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ pThis->BenId = 0;
+ pThis->SemId = create_sem(0, "IPRT Fast Mutex Semaphore");
+ if (pThis->SemId >= B_OK)
+ {
+ *phFastMtx = pThis;
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pThis);
+ return VERR_TOO_MANY_SEMAPHORES; /** @todo r=ramshankar: use RTErrConvertFromHaikuKernReturn */
+}
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ delete_sem(pThis->SemId);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (atomic_add(&pThis->BenId, 1) > 0)
+ acquire_sem(pThis->SemId);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (atomic_add(&pThis->BenId, -1) > 1)
+ release_sem(pThis->SemId);
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/semmutex-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/semmutex-r0drv-haiku.c
new file mode 100644
index 00000000..50680e3c
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/semmutex-r0drv-haiku.c
@@ -0,0 +1,243 @@
+/* $Id: semmutex-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Mutex Semaphores, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the Haiku (sleep) mutex.
+ */
+/* XXX: not optimal, maybe should use the (private)
+ kernel recursive_lock ? (but it's not waitable) */
+typedef struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** Kernel semaphore. */
+ sem_id SemId;
+ /** Current holder */
+ volatile thread_id OwnerId;
+ /** Recursion count */
+ int32 cRecursion;
+} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ AssertCompile(sizeof(RTSEMMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phMutexSem, VERR_INVALID_POINTER);
+
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (RT_UNLIKELY(!pThis))
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ pThis->SemId = create_sem(0, "IPRT Mutex Semaphore");
+ if (pThis->SemId < B_OK)
+ {
+ pThis->OwnerId = -1;
+ pThis->cRecursion = 0;
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+ }
+ RTMemFree(pThis);
+ return VERR_TOO_MANY_SEMAPHORES; /** @todo r=ramshankar: use RTErrConvertFromHaikuKernReturn */
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+
+ delete_sem(pThis->SemId);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker function for acquiring the mutex.
+ *
+ * @param hMutexSem The mutex object.
+ * @param fFlags Mutex flags (see RTSEMWAIT_FLAGS_*)
+ * @param uTimeout Timeout in units specified by the flags.
+ *
+ * @return IPRT status code.
+ */
+static int rtSemMutexRequestEx(RTSEMMUTEX hMutexSem, uint32_t fFlags, uint64_t uTimeout)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ int rc;
+ status_t status;
+ int32 flags = 0;
+ bigtime_t timeout; /* in microseconds */
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (pThis->OwnerId == find_thread(NULL))
+ {
+ pThis->OwnerId++;
+ return VINF_SUCCESS;
+ }
+
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ timeout = B_INFINITE_TIMEOUT;
+ else
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NANOSECS)
+ timeout = uTimeout / 1000;
+ else if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ timeout = uTimeout * 1000;
+ else
+ return VERR_INVALID_PARAMETER;
+
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ flags |= B_RELATIVE_TIMEOUT;
+ else if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
+ flags |= B_ABSOLUTE_TIMEOUT;
+ else
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE)
+ flags |= B_CAN_INTERRUPT;
+
+ status = acquire_sem_etc(pThis->SemId, 1, flags, timeout);
+
+ switch (status)
+ {
+ case B_OK:
+ rc = VINF_SUCCESS;
+ pThis->cRecursion = 1;
+ pThis->OwnerId = find_thread(NULL);
+ break;
+ case B_BAD_SEM_ID:
+ rc = VERR_SEM_DESTROYED;
+ break;
+ case B_INTERRUPTED:
+ rc = VERR_INTERRUPTED;
+ break;
+ case B_WOULD_BLOCK:
+ /* fallthrough? */
+ case B_TIMED_OUT:
+ rc = VERR_TIMEOUT;
+ break;
+ default:
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexRequestEx(hMutexSem, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_RESUME | RTSEMWAIT_FLAGS_MILLISECS, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequest(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexRequestEx(hMutexSem, RTSEMWAIT_FLAGS_RELATIVE | RTSEMWAIT_FLAGS_NORESUME | RTSEMWAIT_FLAGS_MILLISECS, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (pThis->OwnerId != find_thread(NULL))
+ return VERR_INVALID_HANDLE;
+
+ if (--pThis->cRecursion == 0)
+ {
+ pThis->OwnerId == -1;
+ release_sem(pThis->SemId);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), false);
+
+ return pThis->OwnerId != -1;
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/spinlock-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/spinlock-r0drv-haiku.c
new file mode 100644
index 00000000..fae92994
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/spinlock-r0drv-haiku.c
@@ -0,0 +1,148 @@
+/* $Id: spinlock-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/spinlock.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+#include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the KSPIN_LOCK type.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Spinlock creation flags */
+ uint32_t fFlags;
+ /** Saved interrupt CPU status. */
+ cpu_status volatile fIntSaved;
+ /** The Haiku spinlock structure. */
+ spinlock hSpinLock;
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ NOREF(pszName);
+
+ /*
+ * Allocate.
+ */
+ AssertCompile(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ PRTSPINLOCKINTERNAL pSpinlockInt = (PRTSPINLOCKINTERNAL)RTMemAllocZ(sizeof(*pSpinlockInt));
+ if (RT_UNLIKELY(!pSpinlockInt))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize & return.
+ */
+ pSpinlockInt->u32Magic = RTSPINLOCK_MAGIC;
+ pSpinlockInt->fFlags = fFlags;
+ pSpinlockInt->fIntSaved = 0;
+ B_INITIALIZE_SPINLOCK(&pSpinlockInt->hSpinLock);
+
+ *pSpinlock = pSpinlockInt;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ PRTSPINLOCKINTERNAL pSpinlockInt = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (RT_UNLIKELY(!pSpinlockInt))
+ return VERR_INVALID_PARAMETER;
+ AssertMsgReturn(pSpinlockInt->u32Magic == RTSPINLOCK_MAGIC,
+ ("Invalid spinlock %p magic=%#x\n", pSpinlockInt, pSpinlockInt->u32Magic),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Make the lock invalid and release the memory.
+ */
+ ASMAtomicIncU32(&pSpinlockInt->u32Magic);
+
+ B_INITIALIZE_SPINLOCK(&pSpinlockInt->hSpinLock);
+
+ RTMemFree(pSpinlockInt);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pSpinlockInt = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertPtr(pSpinlockInt);
+ Assert(pSpinlockInt->u32Magic == RTSPINLOCK_MAGIC);
+
+ /* Haiku cannot take spinlocks without disabling interrupts. Ignore our spinlock creation flags. */
+ pSpinlockInt->fIntSaved = disable_interrupts();
+ acquire_spinlock(&pSpinlockInt->hSpinLock);
+}
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pSpinlockInt = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertPtr(pSpinlockInt);
+ Assert(pSpinlockInt->u32Magic == RTSPINLOCK_MAGIC);
+
+ release_spinlock(&pSpinlockInt->hSpinLock);
+ restore_interrupts(pSpinlockInt->fIntSaved);
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/the-haiku-kernel.h b/src/VBox/Runtime/r0drv/haiku/the-haiku-kernel.h
new file mode 100644
index 00000000..5b52a0b2
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/the-haiku-kernel.h
@@ -0,0 +1,126 @@
+/* $Id: the-haiku-kernel.h $ */
+/** @file
+ * IPRT - Include all necessary headers for the Haiku kernel.
+ */
+
+/*
+ * 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
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r0drv_haiku_the_haiku_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_haiku_the_haiku_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <stdlib.h>
+
+#include <OS.h>
+#include <KernelExport.h>
+
+#include <iprt/cdefs.h>
+#include <iprt/err.h>
+#include <iprt/types.h>
+
+RT_C_DECLS_BEGIN
+
+/* headers/private/kernel/smp.h */
+
+extern int32 smp_get_num_cpus(void);
+extern int32 smp_get_current_cpu(void);
+
+/* headers/private/kernel/vm/vm.h */
+extern status_t vm_unreserve_address_range(team_id team, void *address, addr_t size);
+extern status_t vm_reserve_address_range(team_id team, void **_address, uint32 addressSpec, addr_t size, uint32 flags);
+extern area_id vm_clone_area(team_id team, const char *name, void **address, uint32 addressSpec, uint32 protection,
+ uint32 mapping, area_id sourceArea, bool kernel);
+
+/* headers/private/kernel/thread_type.h */
+
+extern spinlock gThreadSpinlock;
+#define GRAB_THREAD_LOCK() acquire_spinlock(&gThreadSpinlock)
+#define RELEASE_THREAD_LOCK() release_spinlock(&gThreadSpinlock)
+typedef struct
+{
+ int32 flags; /* summary of events relevant in interrupt handlers (signals pending, user debugging
+ enabled, etc.) */
+#if 0
+ Thread *all_next;
+ Thread *team_next;
+ Thread *queue_next; /* i.e. run queue, release queue, etc. */
+ timer alarm;
+ thread_id id;
+ char name[B_OS_NAME_LENGTH];
+ int32 priority;
+ int32 next_priority;
+ int32 io_priority;
+ int32 state;
+ int32 next_state;
+#endif
+ // and a lot more...
+} Thread;
+
+/* headers/private/kernel/thread.h */
+
+extern Thread* thread_get_thread_struct(thread_id id);
+extern Thread* thread_get_thread_struct_locked(thread_id id);
+
+extern void thread_yield(bool force);
+
+RT_C_DECLS_END
+
+/**
+ * Convert from Haiku kernel return code to IPRT status code.
+ * @todo put this where it belongs! (i.e. in a separate file and prototype in iprt/err.h)
+ * Or as generic call since it's not r0 specific.
+ */
+DECLINLINE(int) RTErrConvertFromHaikuKernReturn(status_t rc)
+{
+ switch (rc)
+ {
+ case B_OK: return VINF_SUCCESS;
+ case B_BAD_SEM_ID: return VERR_SEM_ERROR;
+ case B_NO_MORE_SEMS: return VERR_TOO_MANY_SEMAPHORES;
+ case B_BAD_THREAD_ID: return VERR_INVALID_PARAMETER;
+ case B_NO_MORE_THREADS: return VERR_MAX_THRDS_REACHED;
+ case B_BAD_TEAM_ID: return VERR_INVALID_PARAMETER;
+ case B_NO_MORE_TEAMS: return VERR_MAX_PROCS_REACHED;
+ //default: return VERR_GENERAL_FAILURE;
+ /** POSIX Errors are defined as a subset of system errors. */
+ default: return RTErrConvertFromErrno(rc);
+ }
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_haiku_the_haiku_kernel_h */
+
diff --git a/src/VBox/Runtime/r0drv/haiku/thread-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/thread-r0drv-haiku.c
new file mode 100644
index 00000000..93ed4088
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/thread-r0drv-haiku.c
@@ -0,0 +1,137 @@
+/* $Id: thread-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Threads, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+#include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mp.h>
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)find_thread(NULL);
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ snooze((bigtime_t)cMillies * 1000);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ //FIXME
+ //snooze(0);
+ thread_yield(true);
+ return true; /* this is fishy */
+}
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+
+ //XXX: can't do this, it might actually be held by another cpu
+ //return !B_SPINLOCK_IS_LOCKED(&gThreadSpinlock);
+ return ASMIntAreEnabled(); /** @todo find a better way. */
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+ /** @todo check if Thread::next_priority or
+ * cpu_ent::invoke_scheduler could do. */
+ return false;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* RTThreadPreemptIsPending is not reliable yet. */
+ return false;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+ /* yes, kernel preemption is possible. */
+ return true;
+}
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->uOldCpuState == 0);
+
+ pState->uOldCpuState = (uint32_t)disable_interrupts();
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+}
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+ restore_interrupts((cpu_status)pState->uOldCpuState);
+ pState->uOldCpuState = 0;
+}
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); NOREF(hThread);
+ /** @todo Implement RTThreadIsInInterrupt. Required for guest
+ * additions! */
+ return !ASMIntAreEnabled();
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/thread2-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/thread2-r0drv-haiku.c
new file mode 100644
index 00000000..4763248d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/thread2-r0drv-haiku.c
@@ -0,0 +1,148 @@
+/* $Id: thread2-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+#include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/thread.h"
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ /* No TLS in Ring-0. :-/ */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative((RTNATIVETHREAD)find_thread(NULL));
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ int32 iPriority;
+ status_t status;
+
+ /*
+ * Convert the priority type to native priorities.
+ * (This is quite naive but should be ok.)
+ */
+ switch (enmType)
+ {
+ case RTTHREADTYPE_INFREQUENT_POLLER: iPriority = B_LOWEST_ACTIVE_PRIORITY; break;
+ case RTTHREADTYPE_EMULATION: iPriority = B_LOW_PRIORITY; break;
+ case RTTHREADTYPE_DEFAULT: iPriority = B_NORMAL_PRIORITY; break;
+ case RTTHREADTYPE_MSG_PUMP: iPriority = B_DISPLAY_PRIORITY; break;
+ case RTTHREADTYPE_IO: iPriority = B_URGENT_DISPLAY_PRIORITY; break;
+ case RTTHREADTYPE_TIMER: iPriority = B_REAL_TIME_DISPLAY_PRIORITY; break;
+ default:
+ AssertMsgFailed(("enmType=%d\n", enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ status = set_thread_priority((thread_id)pThread->Core.Key, iPriority);
+
+ return RTErrConvertFromHaikuKernReturn(status);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ /** @todo fix RTThreadWait/RTR0Term race on freebsd. */
+ RTThreadSleep(1);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+/**
+ * Native kernel thread wrapper function.
+ *
+ * This will forward to rtThreadMain and do termination upon return.
+ *
+ * @param pvArg Pointer to the argument package.
+ * @param Ignored Wait result, which we ignore.
+ */
+static status_t rtThreadNativeMain(void *pvArg)
+{
+ const thread_id Self = find_thread(NULL);
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArg;
+
+ int rc = rtThreadMain(pThread, (RTNATIVETHREAD)Self, &pThread->szName[0]);
+
+ if (rc < 0)
+ return RTErrConvertFromHaikuKernReturn(rc);
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+ thread_id NativeThread;
+ RT_ASSERT_PREEMPTIBLE();
+
+ NativeThread = spawn_kernel_thread(rtThreadNativeMain, pThreadInt->szName, B_NORMAL_PRIORITY, pThreadInt);
+ if (NativeThread >= B_OK)
+ {
+ resume_thread(NativeThread);
+ *pNativeThread = (RTNATIVETHREAD)NativeThread;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromHaikuKernReturn(NativeThread);
+}
+
diff --git a/src/VBox/Runtime/r0drv/haiku/time-r0drv-haiku.c b/src/VBox/Runtime/r0drv/haiku/time-r0drv-haiku.c
new file mode 100644
index 00000000..1d1a976e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/haiku/time-r0drv-haiku.c
@@ -0,0 +1,89 @@
+/* $Id: time-r0drv-haiku.c $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, Haiku.
+ */
+
+/*
+ * 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 RTLOGGROUP_TIME
+#include "the-haiku-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/time.h>
+
+#include <iprt/asm.h>
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+ return system_time() * RT_NS_1US;
+}
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemMilliTS(void)
+{
+ return system_time() / RT_NS_1US;
+}
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return rtTimeGetSystemMilliTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemMilliTS();
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ return RTTimeSpecSetNano(pTime, real_time_clock_usecs() * RT_NS_1US);
+}
+
diff --git a/src/VBox/Runtime/r0drv/initterm-r0drv.cpp b/src/VBox/Runtime/r0drv/initterm-r0drv.cpp
new file mode 100644
index 00000000..00a5886a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/initterm-r0drv.cpp
@@ -0,0 +1,174 @@
+/* $Id: initterm-r0drv.cpp $ */
+/** @file
+ * IPRT - Initialization & Termination, R0 Driver, Common.
+ */
+
+/*
+ * 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/initterm.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+#ifndef IN_GUEST /* play safe for now */
+# include "r0drv/mp-r0drv.h"
+# include "r0drv/power-r0drv.h"
+#endif
+
+#include "internal/initterm.h"
+#include "internal/mem.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Count of current IPRT users.
+ * In ring-0 several drivers / kmods / kexts / wossnames may share the
+ * same runtime code. So, we need to keep count in order not to terminate
+ * it prematurely. */
+static int32_t volatile g_crtR0Users = 0;
+
+
+/**
+ * Initializes the ring-0 driver runtime library.
+ *
+ * @returns iprt status code.
+ * @param fReserved Flags reserved for the future.
+ */
+RTR0DECL(int) RTR0Init(unsigned fReserved)
+{
+ int rc;
+ uint32_t cNewUsers;
+ Assert(fReserved == 0); RT_NOREF_PV(fReserved);
+#ifndef RT_OS_SOLARIS /* On Solaris our thread preemption information is only obtained in rtR0InitNative().*/
+ RT_ASSERT_PREEMPTIBLE();
+#endif
+
+ /*
+ * The first user initializes it.
+ * We rely on the module loader to ensure that there are no
+ * initialization races should two modules share the IPRT.
+ */
+ cNewUsers = ASMAtomicIncS32(&g_crtR0Users);
+ if (cNewUsers != 1)
+ {
+ if (cNewUsers > 1)
+ return VINF_SUCCESS;
+ ASMAtomicDecS32(&g_crtR0Users);
+ return VERR_INTERNAL_ERROR_3;
+ }
+
+ rc = rtR0InitNative();
+ if (RT_SUCCESS(rc))
+ {
+#ifdef RTR0MEM_WITH_EF_APIS
+ rtR0MemEfInit();
+#endif
+ rc = rtThreadInit();
+ if (RT_SUCCESS(rc))
+ {
+#ifndef IN_GUEST /* play safe for now */
+ rc = rtR0MpNotificationInit();
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtR0PowerNotificationInit();
+ if (RT_SUCCESS(rc))
+ return rc;
+ rtR0MpNotificationTerm();
+ }
+#else
+ if (RT_SUCCESS(rc))
+ return rc;
+#endif
+ rtThreadTerm();
+ }
+#ifdef RTR0MEM_WITH_EF_APIS
+ rtR0MemEfTerm();
+#endif
+ rtR0TermNative();
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTR0Init);
+
+
+static void rtR0Term(void)
+{
+ rtThreadTerm();
+#ifndef IN_GUEST /* play safe for now */
+ rtR0PowerNotificationTerm();
+ rtR0MpNotificationTerm();
+#endif
+#ifdef RTR0MEM_WITH_EF_APIS
+ rtR0MemEfTerm();
+#endif
+ rtR0TermNative();
+}
+
+
+/**
+ * Terminates the ring-0 driver runtime library.
+ */
+RTR0DECL(void) RTR0Term(void)
+{
+ int32_t cNewUsers;
+ RT_ASSERT_PREEMPTIBLE();
+
+ cNewUsers = ASMAtomicDecS32(&g_crtR0Users);
+ Assert(cNewUsers >= 0);
+ if (cNewUsers == 0)
+ rtR0Term();
+ else if (cNewUsers < 0)
+ ASMAtomicIncS32(&g_crtR0Users);
+}
+RT_EXPORT_SYMBOL(RTR0Term);
+
+
+/* Note! Should *not* be exported since it's only for static linking. */
+RTR0DECL(void) RTR0TermForced(void)
+{
+ RT_ASSERT_PREEMPTIBLE();
+
+ AssertMsg(g_crtR0Users == 1, ("%d\n", g_crtR0Users));
+ ASMAtomicWriteS32(&g_crtR0Users, 0);
+
+ rtR0Term();
+}
+
diff --git a/src/VBox/Runtime/r0drv/linux/Makefile.kup b/src/VBox/Runtime/r0drv/linux/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c
new file mode 100644
index 00000000..7de10556
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/RTLogWriteDebugger-r0drv-linux.c
@@ -0,0 +1,53 @@
+/* $Id: RTLogWriteDebugger-r0drv-linux.c $ */
+/** @file
+ * IPRT - Log To Debugger, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/log.h>
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ printk("%.*s", (int)cb, pch);
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+RT_EXPORT_SYMBOL(RTLogWriteDebugger);
+
diff --git a/src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c
new file mode 100644
index 00000000..47410cca
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/alloc-r0drv-linux.c
@@ -0,0 +1,257 @@
+/* $Id: alloc-r0drv-linux.c $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "r0drv/alloc-r0drv.h"
+
+#include "internal/initterm.h"
+
+
+
+/**
+ * OS specific allocation function.
+ */
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ PRTMEMHDR pHdr;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Allocate.
+ */
+ if (
+#if 1 /* vmalloc has serious performance issues, avoid it. */
+ cb <= PAGE_SIZE*16 - sizeof(*pHdr)
+#else
+ cb <= PAGE_SIZE
+#endif
+ || (fFlags & RTMEMHDR_FLAG_ANY_CTX)
+ )
+ {
+ fFlags |= RTMEMHDR_FLAG_KMALLOC;
+ pHdr = kmalloc(cb + sizeof(*pHdr),
+ fFlags & RTMEMHDR_FLAG_ANY_CTX_ALLOC ? GFP_ATOMIC | __GFP_NOWARN : GFP_KERNEL | __GFP_NOWARN);
+ if (RT_UNLIKELY( !pHdr
+ && cb > PAGE_SIZE
+ && !(fFlags & RTMEMHDR_FLAG_ANY_CTX) ))
+ {
+ fFlags &= ~RTMEMHDR_FLAG_KMALLOC;
+ pHdr = vmalloc(cb + sizeof(*pHdr));
+ }
+ }
+ else
+ pHdr = vmalloc(cb + sizeof(*pHdr));
+ if (RT_LIKELY(pHdr))
+ {
+ /*
+ * Initialize.
+ */
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = cb;
+ pHdr->cbReq = cb;
+
+ *ppHdr = pHdr;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * OS specific free function.
+ */
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ pHdr->u32Magic += 1;
+ if (pHdr->fFlags & RTMEMHDR_FLAG_KMALLOC)
+ kfree(pHdr);
+ else
+ vfree(pHdr);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+
+/**
+ * Compute order. Some functions allocate 2^order pages.
+ *
+ * @returns order.
+ * @param cPages Number of pages.
+ */
+static int CalcPowerOf2Order(unsigned long cPages)
+{
+ int iOrder;
+ unsigned long cTmp;
+
+ for (iOrder = 0, cTmp = cPages; cTmp >>= 1; ++iOrder)
+ ;
+ if (cPages & ~(1 << iOrder))
+ ++iOrder;
+
+ return iOrder;
+}
+
+
+/**
+ * Allocates physical contiguous memory (below 4GB).
+ * The allocation is page aligned and the content is undefined.
+ *
+ * @returns Pointer to the memory block. This is page aligned.
+ * @param pPhys Where to store the physical address.
+ * @param cb The allocation size in bytes. This is always
+ * rounded up to PAGE_SIZE.
+ */
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ int cOrder;
+ unsigned cPages;
+ struct page *paPages;
+ void *pvRet;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * validate input.
+ */
+ AssertPtr(pPhys);
+ Assert(cb > 0);
+
+ /*
+ * Allocate page pointer array.
+ */
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+ cPages = cb >> PAGE_SHIFT;
+ cOrder = CalcPowerOf2Order(cPages);
+#if (defined(RT_ARCH_AMD64) || defined(CONFIG_X86_PAE)) && defined(GFP_DMA32)
+ /* ZONE_DMA32: 0-4GB */
+ paPages = alloc_pages(GFP_DMA32 | __GFP_NOWARN, cOrder);
+ if (!paPages)
+#endif
+#ifdef RT_ARCH_AMD64
+ /* ZONE_DMA; 0-16MB */
+ paPages = alloc_pages(GFP_DMA | __GFP_NOWARN, cOrder);
+#else
+ /* ZONE_NORMAL: 0-896MB */
+ paPages = alloc_pages(GFP_USER | __GFP_NOWARN, cOrder);
+#endif
+ if (paPages)
+ {
+ /*
+ * Reserve the pages and mark them executable.
+ */
+ unsigned iPage;
+ for (iPage = 0; iPage < cPages; iPage++)
+ {
+ Assert(!PageHighMem(&paPages[iPage]));
+ if (iPage + 1 < cPages)
+ {
+ AssertMsg( (uintptr_t)phys_to_virt(page_to_phys(&paPages[iPage])) + PAGE_SIZE
+ == (uintptr_t)phys_to_virt(page_to_phys(&paPages[iPage + 1]))
+ && page_to_phys(&paPages[iPage]) + PAGE_SIZE
+ == page_to_phys(&paPages[iPage + 1]),
+ ("iPage=%i cPages=%u [0]=%#llx,%p [1]=%#llx,%p\n", iPage, cPages,
+ (long long)page_to_phys(&paPages[iPage]), phys_to_virt(page_to_phys(&paPages[iPage])),
+ (long long)page_to_phys(&paPages[iPage + 1]), phys_to_virt(page_to_phys(&paPages[iPage + 1])) ));
+ }
+
+ SetPageReserved(&paPages[iPage]);
+ }
+ *pPhys = page_to_phys(paPages);
+ pvRet = phys_to_virt(page_to_phys(paPages));
+ }
+ else
+ pvRet = NULL;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return pvRet;
+}
+RT_EXPORT_SYMBOL(RTMemContAlloc);
+
+
+/**
+ * Frees memory allocated using RTMemContAlloc().
+ *
+ * @param pv Pointer to return from RTMemContAlloc().
+ * @param cb The cb parameter passed to RTMemContAlloc().
+ */
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
+{
+ if (pv)
+ {
+ int cOrder;
+ unsigned cPages;
+ unsigned iPage;
+ struct page *paPages;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /* validate */
+ AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv));
+ Assert(cb > 0);
+
+ /* calc order and get pages */
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+ cPages = cb >> PAGE_SHIFT;
+ cOrder = CalcPowerOf2Order(cPages);
+ paPages = virt_to_page(pv);
+
+ /*
+ * Restore page attributes freeing the pages.
+ */
+ for (iPage = 0; iPage < cPages; iPage++)
+ {
+ ClearPageReserved(&paPages[iPage]);
+ }
+ __free_pages(paPages, cOrder);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+}
+RT_EXPORT_SYMBOL(RTMemContFree);
+
diff --git a/src/VBox/Runtime/r0drv/linux/assert-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/assert-r0drv-linux.c
new file mode 100644
index 00000000..a7968d06
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/assert-r0drv-linux.c
@@ -0,0 +1,84 @@
+/* $Id: assert-r0drv-linux.c $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+#include <iprt/asm.h>
+
+#include "internal/assert.h"
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ printk(KERN_EMERG
+ "\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%d) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+ char szMsg[256];
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+ printk(KERN_EMERG "%s", szMsg);
+
+ NOREF(fInitial);
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+RTR0DECL(void) RTR0AssertPanicSystem(void)
+{
+ panic("%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+}
+RT_EXPORT_SYMBOL(RTR0AssertPanicSystem);
+
diff --git a/src/VBox/Runtime/r0drv/linux/initterm-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/initterm-r0drv-linux.c
new file mode 100644
index 00000000..cbdb324a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/initterm-r0drv-linux.c
@@ -0,0 +1,138 @@
+/* $Id: initterm-r0drv-linux.c $ */
+/** @file
+ * IPRT - Initialization & Termination, R0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include "internal/initterm.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The IPRT work queue. */
+#if RTLNX_VER_MIN(2,5,41)
+static struct workqueue_struct *g_prtR0LnxWorkQueue;
+#else
+static DECLARE_TASK_QUEUE(g_rtR0LnxWorkQueue);
+#endif
+
+
+/**
+ * Pushes an item onto the IPRT work queue.
+ *
+ * @param pWork The work item.
+ * @param pfnWorker The callback function. It will be called back
+ * with @a pWork as argument.
+ */
+DECLHIDDEN(void) rtR0LnxWorkqueuePush(RTR0LNXWORKQUEUEITEM *pWork, void (*pfnWorker)(RTR0LNXWORKQUEUEITEM *))
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+#if RTLNX_VER_MIN(2,5,41)
+# if RTLNX_VER_MIN(2,6,20)
+ INIT_WORK(pWork, pfnWorker);
+# else
+ INIT_WORK(pWork, (void (*)(void *))pfnWorker, pWork);
+# endif
+ queue_work(g_prtR0LnxWorkQueue, pWork);
+#else
+ INIT_TQUEUE(pWork, (void (*)(void *))pfnWorker, pWork);
+ queue_task(pWork, &g_rtR0LnxWorkQueue);
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+/**
+ * Flushes all items in the IPRT work queue.
+ *
+ * @remarks This is mostly for 2.4.x compatability. Must not be called from
+ * atomic contexts or with unncessary locks held.
+ */
+DECLHIDDEN(void) rtR0LnxWorkqueueFlush(void)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+#if RTLNX_VER_MIN(2,5,41)
+ flush_workqueue(g_prtR0LnxWorkQueue);
+#else
+ run_task_queue(&g_rtR0LnxWorkQueue);
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+
+DECLHIDDEN(int) rtR0InitNative(void)
+{
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+#if RTLNX_VER_MIN(2,5,41)
+ #if RTLNX_VER_MIN(2,6,13)
+ g_prtR0LnxWorkQueue = create_workqueue("iprt-VBoxWQueue");
+ #else
+ g_prtR0LnxWorkQueue = create_workqueue("iprt-VBoxQ");
+ #endif
+ if (!g_prtR0LnxWorkQueue)
+ rc = VERR_NO_MEMORY;
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtR0TermNative(void)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ rtR0LnxWorkqueueFlush();
+#if RTLNX_VER_MIN(2,5,41)
+ destroy_workqueue(g_prtR0LnxWorkQueue);
+ g_prtR0LnxWorkQueue = NULL;
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
diff --git a/src/VBox/Runtime/r0drv/linux/memobj-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/memobj-r0drv-linux.c
new file mode 100644
index 00000000..8342fbf8
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/memobj-r0drv-linux.c
@@ -0,0 +1,2104 @@
+/* $Id: memobj-r0drv-linux.c $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+
+#include <iprt/memobj.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include "internal/memobj.h"
+#include "internal/iprt.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* early 2.6 kernels */
+#ifndef PAGE_SHARED_EXEC
+# define PAGE_SHARED_EXEC PAGE_SHARED
+#endif
+#ifndef PAGE_READONLY_EXEC
+# define PAGE_READONLY_EXEC PAGE_READONLY
+#endif
+
+/** @def IPRT_USE_ALLOC_VM_AREA_FOR_EXEC
+ * Whether we use alloc_vm_area (3.2+) for executable memory.
+ * This is a must for 5.8+, but we enable it all the way back to 3.2.x for
+ * better W^R compliance (fExecutable flag). */
+#if RTLNX_VER_RANGE(3,2,0, 5,10,0) || defined(DOXYGEN_RUNNING)
+# define IPRT_USE_ALLOC_VM_AREA_FOR_EXEC
+#endif
+/** @def IPRT_USE_APPLY_TO_PAGE_RANGE_FOR_EXEC
+ * alloc_vm_area was removed with 5.10 so we have to resort to a different way
+ * to allocate executable memory.
+ * It would be possible to remove IPRT_USE_ALLOC_VM_AREA_FOR_EXEC and use
+ * this path execlusively for 3.2+ but no time to test it really works on every
+ * supported kernel, so better play safe for now.
+ */
+#if RTLNX_VER_MIN(5,10,0) || defined(DOXYGEN_RUNNING)
+# define IPRT_USE_APPLY_TO_PAGE_RANGE_FOR_EXEC
+#endif
+
+/*
+ * 2.6.29+ kernels don't work with remap_pfn_range() anymore because
+ * track_pfn_vma_new() is apparently not defined for non-RAM pages.
+ * It should be safe to use vm_insert_page() older kernels as well.
+ */
+#if RTLNX_VER_MIN(2,6,23)
+# define VBOX_USE_INSERT_PAGE
+#endif
+#if defined(CONFIG_X86_PAE) \
+ && ( defined(HAVE_26_STYLE_REMAP_PAGE_RANGE) \
+ || RTLNX_VER_RANGE(2,6,0, 2,6,11) )
+# define VBOX_USE_PAE_HACK
+#endif
+
+/* gfp_t was introduced in 2.6.14, define it for earlier. */
+#if RTLNX_VER_MAX(2,6,14)
+# define gfp_t unsigned
+#endif
+
+/*
+ * Wrappers around mmap_lock/mmap_sem difference.
+ */
+#if RTLNX_VER_MIN(5,8,0)
+# define LNX_MM_DOWN_READ(a_pMm) down_read(&(a_pMm)->mmap_lock)
+# define LNX_MM_UP_READ(a_pMm) up_read(&(a_pMm)->mmap_lock)
+# define LNX_MM_DOWN_WRITE(a_pMm) down_write(&(a_pMm)->mmap_lock)
+# define LNX_MM_UP_WRITE(a_pMm) up_write(&(a_pMm)->mmap_lock)
+#else
+# define LNX_MM_DOWN_READ(a_pMm) down_read(&(a_pMm)->mmap_sem)
+# define LNX_MM_UP_READ(a_pMm) up_read(&(a_pMm)->mmap_sem)
+# define LNX_MM_DOWN_WRITE(a_pMm) down_write(&(a_pMm)->mmap_sem)
+# define LNX_MM_UP_WRITE(a_pMm) up_write(&(a_pMm)->mmap_sem)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The Linux version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJLNX
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ /** Set if the allocation is contiguous.
+ * This means it has to be given back as one chunk. */
+ bool fContiguous;
+ /** Set if executable allocation. */
+ bool fExecutable;
+ /** Set if we've vmap'ed the memory into ring-0. */
+ bool fMappedToRing0;
+ /** This is non-zero if large page allocation. */
+ uint8_t cLargePageOrder;
+#ifdef IPRT_USE_ALLOC_VM_AREA_FOR_EXEC
+ /** Return from alloc_vm_area() that we now need to use for executable
+ * memory. */
+ struct vm_struct *pArea;
+ /** PTE array that goes along with pArea (must be freed). */
+ pte_t **papPtesForArea;
+#endif
+ /** The pages in the apPages array. */
+ size_t cPages;
+ /** Array of struct page pointers. (variable size) */
+ struct page *apPages[1];
+} RTR0MEMOBJLNX;
+/** Pointer to the linux memory object. */
+typedef RTR0MEMOBJLNX *PRTR0MEMOBJLNX;
+
+
+static void rtR0MemObjLinuxFreePages(PRTR0MEMOBJLNX pMemLnx);
+
+
+/**
+ * Helper that converts from a RTR0PROCESS handle to a linux task.
+ *
+ * @returns The corresponding Linux task.
+ * @param R0Process IPRT ring-0 process handle.
+ */
+static struct task_struct *rtR0ProcessToLinuxTask(RTR0PROCESS R0Process)
+{
+ /** @todo fix rtR0ProcessToLinuxTask!! */
+ /** @todo many (all?) callers currently assume that we return 'current'! */
+ return R0Process == RTR0ProcHandleSelf() ? current : NULL;
+}
+
+
+/**
+ * Compute order. Some functions allocate 2^order pages.
+ *
+ * @returns order.
+ * @param cPages Number of pages.
+ */
+static int rtR0MemObjLinuxOrder(size_t cPages)
+{
+ int iOrder;
+ size_t cTmp;
+
+ for (iOrder = 0, cTmp = cPages; cTmp >>= 1; ++iOrder)
+ ;
+ if (cPages & ~((size_t)1 << iOrder))
+ ++iOrder;
+
+ return iOrder;
+}
+
+
+/**
+ * Converts from RTMEM_PROT_* to Linux PAGE_*.
+ *
+ * @returns Linux page protection constant.
+ * @param fProt The IPRT protection mask.
+ * @param fKernel Whether it applies to kernel or user space.
+ */
+static pgprot_t rtR0MemObjLinuxConvertProt(unsigned fProt, bool fKernel)
+{
+ switch (fProt)
+ {
+ default:
+ AssertMsgFailed(("%#x %d\n", fProt, fKernel)); RT_FALL_THRU();
+ case RTMEM_PROT_NONE:
+ return PAGE_NONE;
+
+ case RTMEM_PROT_READ:
+ return fKernel ? PAGE_KERNEL_RO : PAGE_READONLY;
+
+ case RTMEM_PROT_WRITE:
+ case RTMEM_PROT_WRITE | RTMEM_PROT_READ:
+ return fKernel ? PAGE_KERNEL : PAGE_SHARED;
+
+ case RTMEM_PROT_EXEC:
+ case RTMEM_PROT_EXEC | RTMEM_PROT_READ:
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+ if (fKernel)
+ {
+ pgprot_t fPg = MY_PAGE_KERNEL_EXEC;
+ pgprot_val(fPg) &= ~_PAGE_RW;
+ return fPg;
+ }
+ return PAGE_READONLY_EXEC;
+#else
+ return fKernel ? MY_PAGE_KERNEL_EXEC : PAGE_READONLY_EXEC;
+#endif
+
+ case RTMEM_PROT_WRITE | RTMEM_PROT_EXEC:
+ case RTMEM_PROT_WRITE | RTMEM_PROT_EXEC | RTMEM_PROT_READ:
+ return fKernel ? MY_PAGE_KERNEL_EXEC : PAGE_SHARED_EXEC;
+ }
+}
+
+
+/**
+ * Worker for rtR0MemObjNativeReserveUser and rtR0MemObjNativerMapUser that creates
+ * an empty user space mapping.
+ *
+ * We acquire the mmap_sem/mmap_lock of the task!
+ *
+ * @returns Pointer to the mapping.
+ * (void *)-1 on failure.
+ * @param R3PtrFixed (RTR3PTR)-1 if anywhere, otherwise a specific location.
+ * @param cb The size of the mapping.
+ * @param uAlignment The alignment of the mapping.
+ * @param pTask The Linux task to create this mapping in.
+ * @param fProt The RTMEM_PROT_* mask.
+ */
+static void *rtR0MemObjLinuxDoMmap(RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment, struct task_struct *pTask, unsigned fProt)
+{
+ unsigned fLnxProt;
+ unsigned long ulAddr;
+
+ Assert(pTask == current); /* do_mmap */
+ RT_NOREF_PV(pTask);
+
+ /*
+ * Convert from IPRT protection to mman.h PROT_ and call do_mmap.
+ */
+ fProt &= (RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC);
+ if (fProt == RTMEM_PROT_NONE)
+ fLnxProt = PROT_NONE;
+ else
+ {
+ fLnxProt = 0;
+ if (fProt & RTMEM_PROT_READ)
+ fLnxProt |= PROT_READ;
+ if (fProt & RTMEM_PROT_WRITE)
+ fLnxProt |= PROT_WRITE;
+ if (fProt & RTMEM_PROT_EXEC)
+ fLnxProt |= PROT_EXEC;
+ }
+
+ if (R3PtrFixed != (RTR3PTR)-1)
+ {
+#if RTLNX_VER_MIN(3,5,0)
+ ulAddr = vm_mmap(NULL, R3PtrFixed, cb, fLnxProt, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, 0);
+#else
+ LNX_MM_DOWN_WRITE(pTask->mm);
+ ulAddr = do_mmap(NULL, R3PtrFixed, cb, fLnxProt, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, 0);
+ LNX_MM_UP_WRITE(pTask->mm);
+#endif
+ }
+ else
+ {
+#if RTLNX_VER_MIN(3,5,0)
+ ulAddr = vm_mmap(NULL, 0, cb, fLnxProt, MAP_SHARED | MAP_ANONYMOUS, 0);
+#else
+ LNX_MM_DOWN_WRITE(pTask->mm);
+ ulAddr = do_mmap(NULL, 0, cb, fLnxProt, MAP_SHARED | MAP_ANONYMOUS, 0);
+ LNX_MM_UP_WRITE(pTask->mm);
+#endif
+ if ( !(ulAddr & ~PAGE_MASK)
+ && (ulAddr & (uAlignment - 1)))
+ {
+ /** @todo implement uAlignment properly... We'll probably need to make some dummy mappings to fill
+ * up alignment gaps. This is of course complicated by fragmentation (which we might have cause
+ * ourselves) and further by there begin two mmap strategies (top / bottom). */
+ /* For now, just ignore uAlignment requirements... */
+ }
+ }
+
+
+ if (ulAddr & ~PAGE_MASK) /* ~PAGE_MASK == PAGE_OFFSET_MASK */
+ return (void *)-1;
+ return (void *)ulAddr;
+}
+
+
+/**
+ * Worker that destroys a user space mapping.
+ * Undoes what rtR0MemObjLinuxDoMmap did.
+ *
+ * We acquire the mmap_sem/mmap_lock of the task!
+ *
+ * @param pv The ring-3 mapping.
+ * @param cb The size of the mapping.
+ * @param pTask The Linux task to destroy this mapping in.
+ */
+static void rtR0MemObjLinuxDoMunmap(void *pv, size_t cb, struct task_struct *pTask)
+{
+#if RTLNX_VER_MIN(3,5,0)
+ Assert(pTask == current); RT_NOREF_PV(pTask);
+ vm_munmap((unsigned long)pv, cb);
+#elif defined(USE_RHEL4_MUNMAP)
+ LNX_MM_DOWN_WRITE(pTask->mm);
+ do_munmap(pTask->mm, (unsigned long)pv, cb, 0); /* should it be 1 or 0? */
+ LNX_MM_UP_WRITE(pTask->mm);
+#else
+ LNX_MM_DOWN_WRITE(pTask->mm);
+ do_munmap(pTask->mm, (unsigned long)pv, cb);
+ LNX_MM_UP_WRITE(pTask->mm);
+#endif
+}
+
+
+/**
+ * Internal worker that allocates physical pages and creates the memory object for them.
+ *
+ * @returns IPRT status code.
+ * @param ppMemLnx Where to store the memory object pointer.
+ * @param enmType The object type.
+ * @param cb The number of bytes to allocate.
+ * @param uAlignment The alignment of the physical memory.
+ * Only valid if fContiguous == true, ignored otherwise.
+ * @param fFlagsLnx The page allocation flags (GPFs).
+ * @param fContiguous Whether the allocation must be contiguous.
+ * @param fExecutable Whether the memory must be executable.
+ * @param rcNoMem What to return when we're out of pages.
+ * @param pszTag Allocation tag used for statistics and such.
+ */
+static int rtR0MemObjLinuxAllocPages(PRTR0MEMOBJLNX *ppMemLnx, RTR0MEMOBJTYPE enmType, size_t cb,
+ size_t uAlignment, gfp_t fFlagsLnx, bool fContiguous, bool fExecutable, int rcNoMem,
+ const char *pszTag)
+{
+ size_t iPage;
+ size_t const cPages = cb >> PAGE_SHIFT;
+ struct page *paPages;
+
+ /*
+ * Allocate a memory object structure that's large enough to contain
+ * the page pointer array.
+ */
+ PRTR0MEMOBJLNX pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJLNX, apPages[cPages]), enmType,
+ NULL, cb, pszTag);
+ if (!pMemLnx)
+ return VERR_NO_MEMORY;
+ pMemLnx->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ pMemLnx->cPages = cPages;
+
+ if (cPages > 255)
+ {
+# ifdef __GFP_REPEAT
+ /* Try hard to allocate the memory, but the allocation attempt might fail. */
+ fFlagsLnx |= __GFP_REPEAT;
+# endif
+# ifdef __GFP_NOMEMALLOC
+ /* Introduced with Linux 2.6.12: Don't use emergency reserves */
+ fFlagsLnx |= __GFP_NOMEMALLOC;
+# endif
+ }
+
+ /*
+ * Allocate the pages.
+ * For small allocations we'll try contiguous first and then fall back on page by page.
+ */
+#if RTLNX_VER_MIN(2,4,22)
+ if ( fContiguous
+ || cb <= PAGE_SIZE * 2)
+ {
+# ifdef VBOX_USE_INSERT_PAGE
+ paPages = alloc_pages(fFlagsLnx | __GFP_COMP | __GFP_NOWARN, rtR0MemObjLinuxOrder(cPages));
+# else
+ paPages = alloc_pages(fFlagsLnx | __GFP_NOWARN, rtR0MemObjLinuxOrder(cPages));
+# endif
+ if (paPages)
+ {
+ fContiguous = true;
+ for (iPage = 0; iPage < cPages; iPage++)
+ pMemLnx->apPages[iPage] = &paPages[iPage];
+ }
+ else if (fContiguous)
+ {
+ rtR0MemObjDelete(&pMemLnx->Core);
+ return rcNoMem;
+ }
+ }
+
+ if (!fContiguous)
+ {
+ /** @todo Try use alloc_pages_bulk_array when available, it should be faster
+ * than a alloc_page loop. Put it in #ifdefs similar to
+ * IPRT_USE_APPLY_TO_PAGE_RANGE_FOR_EXEC. */
+ for (iPage = 0; iPage < cPages; iPage++)
+ {
+ pMemLnx->apPages[iPage] = alloc_page(fFlagsLnx | __GFP_NOWARN);
+ if (RT_UNLIKELY(!pMemLnx->apPages[iPage]))
+ {
+ while (iPage-- > 0)
+ __free_page(pMemLnx->apPages[iPage]);
+ rtR0MemObjDelete(&pMemLnx->Core);
+ return rcNoMem;
+ }
+ }
+ }
+
+#else /* < 2.4.22 */
+ /** @todo figure out why we didn't allocate page-by-page on 2.4.21 and older... */
+ paPages = alloc_pages(fFlagsLnx, rtR0MemObjLinuxOrder(cPages));
+ if (!paPages)
+ {
+ rtR0MemObjDelete(&pMemLnx->Core);
+ return rcNoMem;
+ }
+ for (iPage = 0; iPage < cPages; iPage++)
+ {
+ pMemLnx->apPages[iPage] = &paPages[iPage];
+ if (fExecutable)
+ MY_SET_PAGES_EXEC(pMemLnx->apPages[iPage], 1);
+ if (PageHighMem(pMemLnx->apPages[iPage]))
+ BUG();
+ }
+
+ fContiguous = true;
+#endif /* < 2.4.22 */
+ pMemLnx->fContiguous = fContiguous;
+ pMemLnx->fExecutable = fExecutable;
+
+#if RTLNX_VER_MAX(4,5,0)
+ /*
+ * Reserve the pages.
+ *
+ * Linux >= 4.5 with CONFIG_DEBUG_VM panics when setting PG_reserved on compound
+ * pages. According to Michal Hocko this shouldn't be necessary anyway because
+ * as pages which are not on the LRU list are never evictable.
+ */
+ for (iPage = 0; iPage < cPages; iPage++)
+ SetPageReserved(pMemLnx->apPages[iPage]);
+#endif
+
+ /*
+ * Note that the physical address of memory allocated with alloc_pages(flags, order)
+ * is always 2^(PAGE_SHIFT+order)-aligned.
+ */
+ if ( fContiguous
+ && uAlignment > PAGE_SIZE)
+ {
+ /*
+ * Check for alignment constraints. The physical address of memory allocated with
+ * alloc_pages(flags, order) is always 2^(PAGE_SHIFT+order)-aligned.
+ */
+ if (RT_UNLIKELY(page_to_phys(pMemLnx->apPages[0]) & (uAlignment - 1)))
+ {
+ /*
+ * This should never happen!
+ */
+ printk("rtR0MemObjLinuxAllocPages(cb=0x%lx, uAlignment=0x%lx): alloc_pages(..., %d) returned physical memory at 0x%lx!\n",
+ (unsigned long)cb, (unsigned long)uAlignment, rtR0MemObjLinuxOrder(cPages), (unsigned long)page_to_phys(pMemLnx->apPages[0]));
+ rtR0MemObjLinuxFreePages(pMemLnx);
+ return rcNoMem;
+ }
+ }
+
+ *ppMemLnx = pMemLnx;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Frees the physical pages allocated by the rtR0MemObjLinuxAllocPages() call.
+ *
+ * This method does NOT free the object.
+ *
+ * @param pMemLnx The object which physical pages should be freed.
+ */
+static void rtR0MemObjLinuxFreePages(PRTR0MEMOBJLNX pMemLnx)
+{
+ size_t iPage = pMemLnx->cPages;
+ if (iPage > 0)
+ {
+ /*
+ * Restore the page flags.
+ */
+ while (iPage-- > 0)
+ {
+#if RTLNX_VER_MAX(4,5,0)
+ /* See SetPageReserved() in rtR0MemObjLinuxAllocPages() */
+ ClearPageReserved(pMemLnx->apPages[iPage]);
+#endif
+#if RTLNX_VER_MAX(2,4,22)
+ if (pMemLnx->fExecutable)
+ MY_SET_PAGES_NOEXEC(pMemLnx->apPages[iPage], 1);
+#endif
+ }
+
+ /*
+ * Free the pages.
+ */
+#if RTLNX_VER_MIN(2,4,22)
+ if (!pMemLnx->fContiguous)
+ {
+ iPage = pMemLnx->cPages;
+ while (iPage-- > 0)
+ __free_page(pMemLnx->apPages[iPage]);
+ }
+ else
+#endif
+ __free_pages(pMemLnx->apPages[0], rtR0MemObjLinuxOrder(pMemLnx->cPages));
+
+ pMemLnx->cPages = 0;
+ }
+}
+
+
+#ifdef IPRT_USE_APPLY_TO_PAGE_RANGE_FOR_EXEC
+/**
+ * User data passed to the apply_to_page_range() callback.
+ */
+typedef struct LNXAPPLYPGRANGE
+{
+ /** Pointer to the memory object. */
+ PRTR0MEMOBJLNX pMemLnx;
+ /** The page protection flags to apply. */
+ pgprot_t fPg;
+} LNXAPPLYPGRANGE;
+/** Pointer to the user data. */
+typedef LNXAPPLYPGRANGE *PLNXAPPLYPGRANGE;
+/** Pointer to the const user data. */
+typedef const LNXAPPLYPGRANGE *PCLNXAPPLYPGRANGE;
+
+/**
+ * Callback called in apply_to_page_range().
+ *
+ * @returns Linux status code.
+ * @param pPte Pointer to the page table entry for the given address.
+ * @param uAddr The address to apply the new protection to.
+ * @param pvUser The opaque user data.
+ */
+static int rtR0MemObjLinuxApplyPageRange(pte_t *pPte, unsigned long uAddr, void *pvUser)
+{
+ PCLNXAPPLYPGRANGE pArgs = (PCLNXAPPLYPGRANGE)pvUser;
+ PRTR0MEMOBJLNX pMemLnx = pArgs->pMemLnx;
+ size_t idxPg = (uAddr - (unsigned long)pMemLnx->Core.pv) >> PAGE_SHIFT;
+
+ set_pte(pPte, mk_pte(pMemLnx->apPages[idxPg], pArgs->fPg));
+ return 0;
+}
+#endif
+
+
+/**
+ * Maps the allocation into ring-0.
+ *
+ * This will update the RTR0MEMOBJLNX::Core.pv and RTR0MEMOBJ::fMappedToRing0 members.
+ *
+ * Contiguous mappings that isn't in 'high' memory will already be mapped into kernel
+ * space, so we'll use that mapping if possible. If execute access is required, we'll
+ * play safe and do our own mapping.
+ *
+ * @returns IPRT status code.
+ * @param pMemLnx The linux memory object to map.
+ * @param fExecutable Whether execute access is required.
+ */
+static int rtR0MemObjLinuxVMap(PRTR0MEMOBJLNX pMemLnx, bool fExecutable)
+{
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Choose mapping strategy.
+ */
+ bool fMustMap = fExecutable
+ || !pMemLnx->fContiguous;
+ if (!fMustMap)
+ {
+ size_t iPage = pMemLnx->cPages;
+ while (iPage-- > 0)
+ if (PageHighMem(pMemLnx->apPages[iPage]))
+ {
+ fMustMap = true;
+ break;
+ }
+ }
+
+ Assert(!pMemLnx->Core.pv);
+ Assert(!pMemLnx->fMappedToRing0);
+
+ if (fMustMap)
+ {
+ /*
+ * Use vmap - 2.4.22 and later.
+ */
+#if RTLNX_VER_MIN(2,4,22)
+ pgprot_t fPg;
+ pgprot_val(fPg) = _PAGE_PRESENT | _PAGE_RW;
+# ifdef _PAGE_NX
+ if (!fExecutable)
+ pgprot_val(fPg) |= _PAGE_NX;
+# endif
+
+# ifdef IPRT_USE_ALLOC_VM_AREA_FOR_EXEC
+ if (fExecutable)
+ {
+# if RTLNX_VER_MIN(3,2,51)
+ pte_t **papPtes = (pte_t **)kmalloc_array(pMemLnx->cPages, sizeof(papPtes[0]), GFP_KERNEL);
+# else
+ pte_t **papPtes = (pte_t **)kmalloc(pMemLnx->cPages * sizeof(papPtes[0]), GFP_KERNEL);
+# endif
+ if (papPtes)
+ {
+ pMemLnx->pArea = alloc_vm_area(pMemLnx->Core.cb, papPtes); /* Note! pArea->nr_pages is not set. */
+ if (pMemLnx->pArea)
+ {
+ size_t i;
+ Assert(pMemLnx->pArea->size >= pMemLnx->Core.cb); /* Note! includes guard page. */
+ Assert(pMemLnx->pArea->addr);
+# ifdef _PAGE_NX
+ pgprot_val(fPg) |= _PAGE_NX; /* Uses RTR0MemObjProtect to clear NX when memory ready, W^X fashion. */
+# endif
+ pMemLnx->papPtesForArea = papPtes;
+ for (i = 0; i < pMemLnx->cPages; i++)
+ *papPtes[i] = mk_pte(pMemLnx->apPages[i], fPg);
+ pMemLnx->Core.pv = pMemLnx->pArea->addr;
+ pMemLnx->fMappedToRing0 = true;
+ }
+ else
+ {
+ kfree(papPtes);
+ rc = VERR_MAP_FAILED;
+ }
+ }
+ else
+ rc = VERR_MAP_FAILED;
+ }
+ else
+# endif
+ {
+# if defined(IPRT_USE_APPLY_TO_PAGE_RANGE_FOR_EXEC)
+ if (fExecutable)
+ pgprot_val(fPg) |= _PAGE_NX; /* Uses RTR0MemObjProtect to clear NX when memory ready, W^X fashion. */
+# endif
+
+# ifdef VM_MAP
+ pMemLnx->Core.pv = vmap(&pMemLnx->apPages[0], pMemLnx->cPages, VM_MAP, fPg);
+# else
+ pMemLnx->Core.pv = vmap(&pMemLnx->apPages[0], pMemLnx->cPages, VM_ALLOC, fPg);
+# endif
+ if (pMemLnx->Core.pv)
+ pMemLnx->fMappedToRing0 = true;
+ else
+ rc = VERR_MAP_FAILED;
+ }
+#else /* < 2.4.22 */
+ rc = VERR_NOT_SUPPORTED;
+#endif
+ }
+ else
+ {
+ /*
+ * Use the kernel RAM mapping.
+ */
+ pMemLnx->Core.pv = phys_to_virt(page_to_phys(pMemLnx->apPages[0]));
+ Assert(pMemLnx->Core.pv);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Undoes what rtR0MemObjLinuxVMap() did.
+ *
+ * @param pMemLnx The linux memory object.
+ */
+static void rtR0MemObjLinuxVUnmap(PRTR0MEMOBJLNX pMemLnx)
+{
+#if RTLNX_VER_MIN(2,4,22)
+# ifdef IPRT_USE_ALLOC_VM_AREA_FOR_EXEC
+ if (pMemLnx->pArea)
+ {
+# if 0
+ pte_t **papPtes = pMemLnx->papPtesForArea;
+ size_t i;
+ for (i = 0; i < pMemLnx->cPages; i++)
+ *papPtes[i] = 0;
+# endif
+ free_vm_area(pMemLnx->pArea);
+ kfree(pMemLnx->papPtesForArea);
+ pMemLnx->pArea = NULL;
+ pMemLnx->papPtesForArea = NULL;
+ }
+ else
+# endif
+ if (pMemLnx->fMappedToRing0)
+ {
+ Assert(pMemLnx->Core.pv);
+ vunmap(pMemLnx->Core.pv);
+ pMemLnx->fMappedToRing0 = false;
+ }
+#else /* < 2.4.22 */
+ Assert(!pMemLnx->fMappedToRing0);
+#endif
+ pMemLnx->Core.pv = NULL;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ PRTR0MEMOBJLNX pMemLnx = (PRTR0MEMOBJLNX)pMem;
+
+ /*
+ * Release any memory that we've allocated or locked.
+ */
+ switch (pMemLnx->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ rtR0MemObjLinuxVUnmap(pMemLnx);
+ rtR0MemObjLinuxFreePages(pMemLnx);
+ break;
+
+ case RTR0MEMOBJTYPE_LARGE_PAGE:
+ {
+ uint32_t const cLargePages = pMemLnx->Core.cb >> (pMemLnx->cLargePageOrder + PAGE_SHIFT);
+ uint32_t iLargePage;
+ for (iLargePage = 0; iLargePage < cLargePages; iLargePage++)
+ __free_pages(pMemLnx->apPages[iLargePage << pMemLnx->cLargePageOrder], pMemLnx->cLargePageOrder);
+ pMemLnx->cPages = 0;
+
+#ifdef IPRT_USE_ALLOC_VM_AREA_FOR_EXEC
+ Assert(!pMemLnx->pArea);
+ Assert(!pMemLnx->papPtesForArea);
+#endif
+ break;
+ }
+
+ case RTR0MEMOBJTYPE_LOCK:
+ if (pMemLnx->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ {
+ struct task_struct *pTask = rtR0ProcessToLinuxTask(pMemLnx->Core.u.Lock.R0Process);
+ size_t iPage;
+ Assert(pTask);
+ if (pTask && pTask->mm)
+ LNX_MM_DOWN_READ(pTask->mm);
+
+ iPage = pMemLnx->cPages;
+ while (iPage-- > 0)
+ {
+ if (!PageReserved(pMemLnx->apPages[iPage]))
+ SetPageDirty(pMemLnx->apPages[iPage]);
+#if RTLNX_VER_MIN(4,6,0)
+ put_page(pMemLnx->apPages[iPage]);
+#else
+ page_cache_release(pMemLnx->apPages[iPage]);
+#endif
+ }
+
+ if (pTask && pTask->mm)
+ LNX_MM_UP_READ(pTask->mm);
+ }
+ /* else: kernel memory - nothing to do here. */
+ break;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ Assert(pMemLnx->Core.pv);
+ if (pMemLnx->Core.u.ResVirt.R0Process != NIL_RTR0PROCESS)
+ {
+ struct task_struct *pTask = rtR0ProcessToLinuxTask(pMemLnx->Core.u.Lock.R0Process);
+ Assert(pTask);
+ if (pTask && pTask->mm)
+ rtR0MemObjLinuxDoMunmap(pMemLnx->Core.pv, pMemLnx->Core.cb, pTask);
+ }
+ else
+ {
+ vunmap(pMemLnx->Core.pv);
+
+ Assert(pMemLnx->cPages == 1 && pMemLnx->apPages[0] != NULL);
+ __free_page(pMemLnx->apPages[0]);
+ pMemLnx->apPages[0] = NULL;
+ pMemLnx->cPages = 0;
+ }
+ pMemLnx->Core.pv = NULL;
+ break;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ Assert(pMemLnx->cPages == 0); Assert(pMemLnx->Core.pv);
+ if (pMemLnx->Core.u.ResVirt.R0Process != NIL_RTR0PROCESS)
+ {
+ struct task_struct *pTask = rtR0ProcessToLinuxTask(pMemLnx->Core.u.Lock.R0Process);
+ Assert(pTask);
+ if (pTask && pTask->mm)
+ rtR0MemObjLinuxDoMunmap(pMemLnx->Core.pv, pMemLnx->Core.cb, pTask);
+ }
+ else
+ vunmap(pMemLnx->Core.pv);
+ pMemLnx->Core.pv = NULL;
+ break;
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemLnx->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC();
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ PRTR0MEMOBJLNX pMemLnx;
+ int rc;
+
+#if RTLNX_VER_MIN(2,4,22)
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_PAGE, cb, PAGE_SIZE, GFP_HIGHUSER,
+ false /* non-contiguous */, fExecutable, VERR_NO_MEMORY, pszTag);
+#else
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_PAGE, cb, PAGE_SIZE, GFP_USER,
+ false /* non-contiguous */, fExecutable, VERR_NO_MEMORY, pszTag);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtR0MemObjLinuxVMap(pMemLnx, fExecutable);
+ if (RT_SUCCESS(rc))
+ {
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ rtR0MemObjLinuxFreePages(pMemLnx);
+ rtR0MemObjDelete(&pMemLnx->Core);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+#ifdef GFP_TRANSHUGE
+ /*
+ * Allocate a memory object structure that's large enough to contain
+ * the page pointer array.
+ */
+# ifdef __GFP_MOVABLE
+ unsigned const fGfp = (GFP_TRANSHUGE | __GFP_ZERO) & ~__GFP_MOVABLE;
+# else
+ unsigned const fGfp = (GFP_TRANSHUGE | __GFP_ZERO);
+# endif
+ size_t const cPagesPerLarge = cbLargePage >> PAGE_SHIFT;
+ unsigned const cLargePageOrder = rtR0MemObjLinuxOrder(cPagesPerLarge);
+ size_t const cLargePages = cb >> (cLargePageOrder + PAGE_SHIFT);
+ size_t const cPages = cb >> PAGE_SHIFT;
+ PRTR0MEMOBJLNX pMemLnx;
+
+ Assert(RT_BIT_64(cLargePageOrder + PAGE_SHIFT) == cbLargePage);
+ pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJLNX, apPages[cPages]),
+ RTR0MEMOBJTYPE_LARGE_PAGE, NULL, cb, pszTag);
+ if (pMemLnx)
+ {
+ size_t iLargePage;
+
+ pMemLnx->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ pMemLnx->cLargePageOrder = cLargePageOrder;
+ pMemLnx->cPages = cPages;
+
+ /*
+ * Allocate the requested number of large pages.
+ */
+ for (iLargePage = 0; iLargePage < cLargePages; iLargePage++)
+ {
+ struct page *paPages = alloc_pages(fGfp, cLargePageOrder);
+ if (paPages)
+ {
+ size_t const iPageBase = iLargePage << cLargePageOrder;
+ size_t iPage = cPagesPerLarge;
+ while (iPage-- > 0)
+ pMemLnx->apPages[iPageBase + iPage] = &paPages[iPage];
+ }
+ else
+ {
+ /*Log(("rtR0MemObjNativeAllocLarge: cb=%#zx cPages=%#zx cLargePages=%#zx cLargePageOrder=%u cPagesPerLarge=%#zx iLargePage=%#zx -> failed!\n",
+ cb, cPages, cLargePages, cLargePageOrder, cPagesPerLarge, iLargePage, paPages));*/
+ while (iLargePage-- > 0)
+ __free_pages(pMemLnx->apPages[iLargePage << (cLargePageOrder - PAGE_SHIFT)], cLargePageOrder);
+ rtR0MemObjDelete(&pMemLnx->Core);
+ return VERR_NO_MEMORY;
+ }
+ }
+ *ppMem = &pMemLnx->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+
+#else
+ /*
+ * We don't call rtR0MemObjFallbackAllocLarge here as it can be a really
+ * bad idea to trigger the swap daemon and whatnot. So, just fail.
+ */
+ RT_NOREF(ppMem, cb, cbLargePage, fFlags, pszTag);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ PRTR0MEMOBJLNX pMemLnx;
+ int rc;
+
+ /* Try to avoid GFP_DMA. GFM_DMA32 was introduced with Linux 2.6.15. */
+#if (defined(RT_ARCH_AMD64) || defined(CONFIG_X86_PAE)) && defined(GFP_DMA32)
+ /* ZONE_DMA32: 0-4GB */
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_LOW, cb, PAGE_SIZE, GFP_DMA32,
+ false /* non-contiguous */, fExecutable, VERR_NO_LOW_MEMORY, pszTag);
+ if (RT_FAILURE(rc))
+#endif
+#ifdef RT_ARCH_AMD64
+ /* ZONE_DMA: 0-16MB */
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_LOW, cb, PAGE_SIZE, GFP_DMA,
+ false /* non-contiguous */, fExecutable, VERR_NO_LOW_MEMORY, pszTag);
+#else
+# ifdef CONFIG_X86_PAE
+# endif
+ /* ZONE_NORMAL: 0-896MB */
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_LOW, cb, PAGE_SIZE, GFP_USER,
+ false /* non-contiguous */, fExecutable, VERR_NO_LOW_MEMORY, pszTag);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtR0MemObjLinuxVMap(pMemLnx, fExecutable);
+ if (RT_SUCCESS(rc))
+ {
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ rtR0MemObjLinuxFreePages(pMemLnx);
+ rtR0MemObjDelete(&pMemLnx->Core);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ PRTR0MEMOBJLNX pMemLnx;
+ int rc;
+
+#if (defined(RT_ARCH_AMD64) || defined(CONFIG_X86_PAE)) && defined(GFP_DMA32)
+ /* ZONE_DMA32: 0-4GB */
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_CONT, cb, PAGE_SIZE, GFP_DMA32,
+ true /* contiguous */, fExecutable, VERR_NO_CONT_MEMORY, pszTag);
+ if (RT_FAILURE(rc))
+#endif
+#ifdef RT_ARCH_AMD64
+ /* ZONE_DMA: 0-16MB */
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_CONT, cb, PAGE_SIZE, GFP_DMA,
+ true /* contiguous */, fExecutable, VERR_NO_CONT_MEMORY, pszTag);
+#else
+ /* ZONE_NORMAL (32-bit hosts): 0-896MB */
+ rc = rtR0MemObjLinuxAllocPages(&pMemLnx, RTR0MEMOBJTYPE_CONT, cb, PAGE_SIZE, GFP_USER,
+ true /* contiguous */, fExecutable, VERR_NO_CONT_MEMORY, pszTag);
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtR0MemObjLinuxVMap(pMemLnx, fExecutable);
+ if (RT_SUCCESS(rc))
+ {
+#if defined(RT_STRICT) && (defined(RT_ARCH_AMD64) || defined(CONFIG_HIGHMEM64G))
+ size_t iPage = pMemLnx->cPages;
+ while (iPage-- > 0)
+ Assert(page_to_phys(pMemLnx->apPages[iPage]) < _4G);
+#endif
+ pMemLnx->Core.u.Cont.Phys = page_to_phys(pMemLnx->apPages[0]);
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ rtR0MemObjLinuxFreePages(pMemLnx);
+ rtR0MemObjDelete(&pMemLnx->Core);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+/**
+ * Worker for rtR0MemObjLinuxAllocPhysSub that tries one allocation strategy.
+ *
+ * @returns IPRT status code.
+ * @param ppMemLnx Where to
+ * @param enmType The object type.
+ * @param cb The size of the allocation.
+ * @param uAlignment The alignment of the physical memory.
+ * Only valid for fContiguous == true, ignored otherwise.
+ * @param PhysHighest See rtR0MemObjNativeAllocPhys.
+ * @param pszTag Allocation tag used for statistics and such.
+ * @param fGfp The Linux GFP flags to use for the allocation.
+ */
+static int rtR0MemObjLinuxAllocPhysSub2(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJTYPE enmType,
+ size_t cb, size_t uAlignment, RTHCPHYS PhysHighest, const char *pszTag, gfp_t fGfp)
+{
+ PRTR0MEMOBJLNX pMemLnx;
+ int rc = rtR0MemObjLinuxAllocPages(&pMemLnx, enmType, cb, uAlignment, fGfp,
+ enmType == RTR0MEMOBJTYPE_PHYS /* contiguous / non-contiguous */,
+ false /*fExecutable*/, VERR_NO_PHYS_MEMORY, pszTag);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Check the addresses if necessary. (Can be optimized a bit for PHYS.)
+ */
+ if (PhysHighest != NIL_RTHCPHYS)
+ {
+ size_t iPage = pMemLnx->cPages;
+ while (iPage-- > 0)
+ if (page_to_phys(pMemLnx->apPages[iPage]) > PhysHighest)
+ {
+ rtR0MemObjLinuxFreePages(pMemLnx);
+ rtR0MemObjDelete(&pMemLnx->Core);
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ /*
+ * Complete the object.
+ */
+ if (enmType == RTR0MEMOBJTYPE_PHYS)
+ {
+ pMemLnx->Core.u.Phys.PhysBase = page_to_phys(pMemLnx->apPages[0]);
+ pMemLnx->Core.u.Phys.fAllocated = true;
+ }
+ *ppMem = &pMemLnx->Core;
+ return rc;
+}
+
+
+/**
+ * Worker for rtR0MemObjNativeAllocPhys and rtR0MemObjNativeAllocPhysNC.
+ *
+ * @returns IPRT status code.
+ * @param ppMem Where to store the memory object pointer on success.
+ * @param enmType The object type.
+ * @param cb The size of the allocation.
+ * @param uAlignment The alignment of the physical memory.
+ * Only valid for enmType == RTR0MEMOBJTYPE_PHYS, ignored otherwise.
+ * @param PhysHighest See rtR0MemObjNativeAllocPhys.
+ * @param pszTag Allocation tag used for statistics and such.
+ */
+static int rtR0MemObjLinuxAllocPhysSub(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJTYPE enmType,
+ size_t cb, size_t uAlignment, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * There are two clear cases and that's the <=16MB and anything-goes ones.
+ * When the physical address limit is somewhere in-between those two we'll
+ * just have to try, starting with HIGHUSER and working our way thru the
+ * different types, hoping we'll get lucky.
+ *
+ * We should probably move this physical address restriction logic up to
+ * the page alloc function as it would be more efficient there. But since
+ * we don't expect this to be a performance issue just yet it can wait.
+ */
+ if (PhysHighest == NIL_RTHCPHYS)
+ /* ZONE_HIGHMEM: the whole physical memory */
+ rc = rtR0MemObjLinuxAllocPhysSub2(ppMem, enmType, cb, uAlignment, PhysHighest, pszTag, GFP_HIGHUSER);
+ else if (PhysHighest <= _1M * 16)
+ /* ZONE_DMA: 0-16MB */
+ rc = rtR0MemObjLinuxAllocPhysSub2(ppMem, enmType, cb, uAlignment, PhysHighest, pszTag, GFP_DMA);
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ if (RT_FAILURE(rc))
+ /* ZONE_HIGHMEM: the whole physical memory */
+ rc = rtR0MemObjLinuxAllocPhysSub2(ppMem, enmType, cb, uAlignment, PhysHighest, pszTag, GFP_HIGHUSER);
+ if (RT_FAILURE(rc))
+ /* ZONE_NORMAL: 0-896MB */
+ rc = rtR0MemObjLinuxAllocPhysSub2(ppMem, enmType, cb, uAlignment, PhysHighest, pszTag, GFP_USER);
+#ifdef GFP_DMA32
+ if (RT_FAILURE(rc))
+ /* ZONE_DMA32: 0-4GB */
+ rc = rtR0MemObjLinuxAllocPhysSub2(ppMem, enmType, cb, uAlignment, PhysHighest, pszTag, GFP_DMA32);
+#endif
+ if (RT_FAILURE(rc))
+ /* ZONE_DMA: 0-16MB */
+ rc = rtR0MemObjLinuxAllocPhysSub2(ppMem, enmType, cb, uAlignment, PhysHighest, pszTag, GFP_DMA);
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+/**
+ * Translates a kernel virtual address to a linux page structure by walking the
+ * page tables.
+ *
+ * @note We do assume that the page tables will not change as we are walking
+ * them. This assumption is rather forced by the fact that I could not
+ * immediately see any way of preventing this from happening. So, we
+ * take some extra care when accessing them.
+ *
+ * Because of this, we don't want to use this function on memory where
+ * attribute changes to nearby pages is likely to cause large pages to
+ * be used or split up. So, don't use this for the linear mapping of
+ * physical memory.
+ *
+ * @returns Pointer to the page structur or NULL if it could not be found.
+ * @param pv The kernel virtual address.
+ */
+RTDECL(struct page *) rtR0MemObjLinuxVirtToPage(void *pv)
+{
+ unsigned long ulAddr = (unsigned long)pv;
+ unsigned long pfn;
+ struct page *pPage;
+ pte_t *pEntry;
+ union
+ {
+ pgd_t Global;
+#if RTLNX_VER_MIN(4,12,0)
+ p4d_t Four;
+#endif
+#if RTLNX_VER_MIN(2,6,11)
+ pud_t Upper;
+#endif
+ pmd_t Middle;
+ pte_t Entry;
+ } u;
+
+ /* Should this happen in a situation this code will be called in? And if
+ * so, can it change under our feet? See also
+ * "Documentation/vm/active_mm.txt" in the kernel sources. */
+ if (RT_UNLIKELY(!current->active_mm))
+ return NULL;
+ u.Global = *pgd_offset(current->active_mm, ulAddr);
+ if (RT_UNLIKELY(pgd_none(u.Global)))
+ return NULL;
+#if RTLNX_VER_MIN(2,6,11)
+# if RTLNX_VER_MIN(4,12,0)
+ u.Four = *p4d_offset(&u.Global, ulAddr);
+ if (RT_UNLIKELY(p4d_none(u.Four)))
+ return NULL;
+ if (p4d_large(u.Four))
+ {
+ pPage = p4d_page(u.Four);
+ AssertReturn(pPage, NULL);
+ pfn = page_to_pfn(pPage); /* doing the safe way... */
+ AssertCompile(P4D_SHIFT - PAGE_SHIFT < 31);
+ pfn += (ulAddr >> PAGE_SHIFT) & ((UINT32_C(1) << (P4D_SHIFT - PAGE_SHIFT)) - 1);
+ return pfn_to_page(pfn);
+ }
+ u.Upper = *pud_offset(&u.Four, ulAddr);
+# else /* < 4.12 */
+ u.Upper = *pud_offset(&u.Global, ulAddr);
+# endif /* < 4.12 */
+ if (RT_UNLIKELY(pud_none(u.Upper)))
+ return NULL;
+# if RTLNX_VER_MIN(2,6,25)
+ if (pud_large(u.Upper))
+ {
+ pPage = pud_page(u.Upper);
+ AssertReturn(pPage, NULL);
+ pfn = page_to_pfn(pPage); /* doing the safe way... */
+ pfn += (ulAddr >> PAGE_SHIFT) & ((UINT32_C(1) << (PUD_SHIFT - PAGE_SHIFT)) - 1);
+ return pfn_to_page(pfn);
+ }
+# endif
+ u.Middle = *pmd_offset(&u.Upper, ulAddr);
+#else /* < 2.6.11 */
+ u.Middle = *pmd_offset(&u.Global, ulAddr);
+#endif /* < 2.6.11 */
+ if (RT_UNLIKELY(pmd_none(u.Middle)))
+ return NULL;
+#if RTLNX_VER_MIN(2,6,0)
+ if (pmd_large(u.Middle))
+ {
+ pPage = pmd_page(u.Middle);
+ AssertReturn(pPage, NULL);
+ pfn = page_to_pfn(pPage); /* doing the safe way... */
+ pfn += (ulAddr >> PAGE_SHIFT) & ((UINT32_C(1) << (PMD_SHIFT - PAGE_SHIFT)) - 1);
+ return pfn_to_page(pfn);
+ }
+#endif
+
+#if RTLNX_VER_MIN(6,5,0) || RTLNX_RHEL_RANGE(9,4, 9,99)
+ pEntry = __pte_map(&u.Middle, ulAddr);
+#elif RTLNX_VER_MIN(2,5,5) || defined(pte_offset_map) /* As usual, RHEL 3 had pte_offset_map earlier. */
+ pEntry = pte_offset_map(&u.Middle, ulAddr);
+#else
+ pEntry = pte_offset(&u.Middle, ulAddr);
+#endif
+ if (RT_UNLIKELY(!pEntry))
+ return NULL;
+ u.Entry = *pEntry;
+#if RTLNX_VER_MIN(2,5,5) || defined(pte_offset_map)
+ pte_unmap(pEntry);
+#endif
+
+ if (RT_UNLIKELY(!pte_present(u.Entry)))
+ return NULL;
+ return pte_page(u.Entry);
+}
+RT_EXPORT_SYMBOL(rtR0MemObjLinuxVirtToPage);
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment,
+ const char *pszTag)
+{
+ return rtR0MemObjLinuxAllocPhysSub(ppMem, RTR0MEMOBJTYPE_PHYS, cb, uAlignment, PhysHighest, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ return rtR0MemObjLinuxAllocPhysSub(ppMem, RTR0MEMOBJTYPE_PHYS_NC, cb, PAGE_SIZE, PhysHighest, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy,
+ const char *pszTag)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * All we need to do here is to validate that we can use
+ * ioremap on the specified address (32/64-bit dma_addr_t).
+ */
+ PRTR0MEMOBJLNX pMemLnx;
+ dma_addr_t PhysAddr = Phys;
+ AssertMsgReturn(PhysAddr == Phys, ("%#llx\n", (unsigned long long)Phys), VERR_ADDRESS_TOO_BIG);
+
+ pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(sizeof(*pMemLnx), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag);
+ if (!pMemLnx)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+
+ pMemLnx->Core.u.Phys.PhysBase = PhysAddr;
+ pMemLnx->Core.u.Phys.fAllocated = false;
+ pMemLnx->Core.u.Phys.uCachePolicy = uCachePolicy;
+ Assert(!pMemLnx->cPages);
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+/* openSUSE Leap 42.3 detection :-/ */
+#if RTLNX_VER_RANGE(4,4,0, 4,6,0) && defined(FAULT_FLAG_REMOTE)
+# define GET_USER_PAGES_API KERNEL_VERSION(4, 10, 0) /* no typo! */
+#else
+# define GET_USER_PAGES_API LINUX_VERSION_CODE
+#endif
+
+DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ const int cPages = cb >> PAGE_SHIFT;
+ struct task_struct *pTask = rtR0ProcessToLinuxTask(R0Process);
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+ struct vm_area_struct **papVMAs;
+# endif
+ PRTR0MEMOBJLNX pMemLnx;
+ int rc = VERR_NO_MEMORY;
+ int const fWrite = fAccess & RTMEM_PROT_WRITE ? 1 : 0;
+
+ /*
+ * Check for valid task and size overflows.
+ */
+ if (!pTask)
+ return VERR_NOT_SUPPORTED;
+ if (((size_t)cPages << PAGE_SHIFT) != cb)
+ return VERR_OUT_OF_RANGE;
+
+ /*
+ * Allocate the memory object and a temporary buffer for the VMAs.
+ */
+ pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJLNX, apPages[cPages]), RTR0MEMOBJTYPE_LOCK,
+ (void *)R3Ptr, cb, pszTag);
+ if (!pMemLnx)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+ papVMAs = (struct vm_area_struct **)RTMemAlloc(sizeof(*papVMAs) * cPages);
+ if (papVMAs)
+ {
+# endif
+ LNX_MM_DOWN_READ(pTask->mm);
+
+ /*
+ * Get user pages.
+ */
+/** @todo r=bird: Should we not force read access too? */
+#if GET_USER_PAGES_API >= KERNEL_VERSION(4, 6, 0)
+ if (R0Process == RTR0ProcHandleSelf())
+ rc = get_user_pages(R3Ptr, /* Where from. */
+ cPages, /* How many pages. */
+# if GET_USER_PAGES_API >= KERNEL_VERSION(4, 9, 0)
+ fWrite ? FOLL_WRITE | /* Write to memory. */
+ FOLL_FORCE /* force write access. */
+ : 0, /* Write to memory. */
+# else
+ fWrite, /* Write to memory. */
+ fWrite, /* force write access. */
+# endif
+ &pMemLnx->apPages[0] /* Page array. */
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+ , papVMAs /* vmas */
+# endif
+ );
+ /*
+ * Actually this should not happen at the moment as call this function
+ * only for our own process.
+ */
+ else
+ rc = get_user_pages_remote(
+# if GET_USER_PAGES_API < KERNEL_VERSION(5, 9, 0)
+ pTask, /* Task for fault accounting. */
+# endif
+ pTask->mm, /* Whose pages. */
+ R3Ptr, /* Where from. */
+ cPages, /* How many pages. */
+# if GET_USER_PAGES_API >= KERNEL_VERSION(4, 9, 0)
+ fWrite ? FOLL_WRITE | /* Write to memory. */
+ FOLL_FORCE /* force write access. */
+ : 0, /* Write to memory. */
+# else
+ fWrite, /* Write to memory. */
+ fWrite, /* force write access. */
+# endif
+ &pMemLnx->apPages[0] /* Page array. */
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+ , papVMAs /* vmas */
+# endif
+# if GET_USER_PAGES_API >= KERNEL_VERSION(4, 10, 0)
+ , NULL /* locked */
+# endif
+ );
+#else /* GET_USER_PAGES_API < KERNEL_VERSION(4, 6, 0) */
+ rc = get_user_pages(pTask, /* Task for fault accounting. */
+ pTask->mm, /* Whose pages. */
+ R3Ptr, /* Where from. */
+ cPages, /* How many pages. */
+/* The get_user_pages API change was back-ported to 4.4.168. */
+# if RTLNX_VER_RANGE(4,4,168, 4,5,0)
+ fWrite ? FOLL_WRITE | /* Write to memory. */
+ FOLL_FORCE /* force write access. */
+ : 0, /* Write to memory. */
+# else
+ fWrite, /* Write to memory. */
+ fWrite, /* force write access. */
+# endif
+ &pMemLnx->apPages[0] /* Page array. */
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+ , papVMAs /* vmas */
+# endif
+ );
+#endif /* GET_USER_PAGES_API < KERNEL_VERSION(4, 6, 0) */
+ if (rc == cPages)
+ {
+ /*
+ * Flush dcache (required?), protect against fork and _really_ pin the page
+ * table entries. get_user_pages() will protect against swapping out the
+ * pages but it will NOT protect against removing page table entries. This
+ * can be achieved with
+ * - using mlock / mmap(..., MAP_LOCKED, ...) from userland. This requires
+ * an appropriate limit set up with setrlimit(..., RLIMIT_MEMLOCK, ...).
+ * Usual Linux distributions support only a limited size of locked pages
+ * (e.g. 32KB).
+ * - setting the PageReserved bit (as we do in rtR0MemObjLinuxAllocPages()
+ * or by
+ * - setting the VM_LOCKED flag. This is the same as doing mlock() without
+ * a range check.
+ */
+ /** @todo The Linux fork() protection will require more work if this API
+ * is to be used for anything but locking VM pages. */
+ while (rc-- > 0)
+ {
+ flush_dcache_page(pMemLnx->apPages[rc]);
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+# if RTLNX_VER_MIN(6,3,0)
+ vm_flags_set(papVMAs[rc], VM_DONTCOPY | VM_LOCKED);
+# else
+ papVMAs[rc]->vm_flags |= VM_DONTCOPY | VM_LOCKED;
+# endif
+# endif
+ }
+
+ LNX_MM_UP_READ(pTask->mm);
+
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+ RTMemFree(papVMAs);
+# endif
+
+ pMemLnx->Core.u.Lock.R0Process = R0Process;
+ pMemLnx->cPages = cPages;
+ Assert(!pMemLnx->fMappedToRing0);
+ *ppMem = &pMemLnx->Core;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Failed - we need to unlock any pages that we succeeded to lock.
+ */
+ while (rc-- > 0)
+ {
+ if (!PageReserved(pMemLnx->apPages[rc]))
+ SetPageDirty(pMemLnx->apPages[rc]);
+#if RTLNX_VER_MIN(4,6,0)
+ put_page(pMemLnx->apPages[rc]);
+#else
+ page_cache_release(pMemLnx->apPages[rc]);
+#endif
+ }
+
+ LNX_MM_UP_READ(pTask->mm);
+
+ rc = VERR_LOCK_FAILED;
+
+# if GET_USER_PAGES_API < KERNEL_VERSION(6, 5, 0)
+ RTMemFree(papVMAs);
+ }
+# endif
+
+ rtR0MemObjDelete(&pMemLnx->Core);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ void *pvLast = (uint8_t *)pv + cb - 1;
+ size_t const cPages = cb >> PAGE_SHIFT;
+ PRTR0MEMOBJLNX pMemLnx;
+ bool fLinearMapping;
+ int rc;
+ uint8_t *pbPage;
+ size_t iPage;
+ NOREF(fAccess);
+
+ if ( !RTR0MemKernelIsValidAddr(pv)
+ || !RTR0MemKernelIsValidAddr(pv + cb))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * The lower part of the kernel memory has a linear mapping between
+ * physical and virtual addresses. So we take a short cut here. This is
+ * assumed to be the cleanest way to handle those addresses (and the code
+ * is well tested, though the test for determining it is not very nice).
+ * If we ever decide it isn't we can still remove it.
+ */
+#if 0
+ fLinearMapping = (unsigned long)pvLast < VMALLOC_START;
+#else
+ fLinearMapping = (unsigned long)pv >= (unsigned long)__va(0)
+ && (unsigned long)pvLast < (unsigned long)high_memory;
+#endif
+
+ /*
+ * Allocate the memory object.
+ */
+ pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJLNX, apPages[cPages]), RTR0MEMOBJTYPE_LOCK,
+ pv, cb, pszTag);
+ if (!pMemLnx)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+
+ /*
+ * Gather the pages.
+ * We ASSUME all kernel pages are non-swappable and non-movable.
+ */
+ rc = VINF_SUCCESS;
+ pbPage = (uint8_t *)pvLast;
+ iPage = cPages;
+ if (!fLinearMapping)
+ {
+ while (iPage-- > 0)
+ {
+ struct page *pPage = rtR0MemObjLinuxVirtToPage(pbPage);
+ if (RT_UNLIKELY(!pPage))
+ {
+ rc = VERR_LOCK_FAILED;
+ break;
+ }
+ pMemLnx->apPages[iPage] = pPage;
+ pbPage -= PAGE_SIZE;
+ }
+ }
+ else
+ {
+ while (iPage-- > 0)
+ {
+ pMemLnx->apPages[iPage] = virt_to_page(pbPage);
+ pbPage -= PAGE_SIZE;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Complete the memory object and return.
+ */
+ pMemLnx->Core.u.Lock.R0Process = NIL_RTR0PROCESS;
+ pMemLnx->cPages = cPages;
+ Assert(!pMemLnx->fMappedToRing0);
+ *ppMem = &pMemLnx->Core;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ rtR0MemObjDelete(&pMemLnx->Core);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ const char *pszTag)
+{
+#if RTLNX_VER_MIN(2,4,22)
+ IPRT_LINUX_SAVE_EFL_AC();
+ const size_t cPages = cb >> PAGE_SHIFT;
+ struct page *pDummyPage;
+ struct page **papPages;
+
+ /* check for unsupported stuff. */
+ AssertMsgReturn(pvFixed == (void *)-1, ("%p\n", pvFixed), VERR_NOT_SUPPORTED);
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Allocate a dummy page and create a page pointer array for vmap such that
+ * the dummy page is mapped all over the reserved area.
+ */
+ pDummyPage = alloc_page(GFP_HIGHUSER | __GFP_NOWARN);
+ if (pDummyPage)
+ {
+ papPages = RTMemAlloc(sizeof(*papPages) * cPages);
+ if (papPages)
+ {
+ void *pv;
+ size_t iPage = cPages;
+ while (iPage-- > 0)
+ papPages[iPage] = pDummyPage;
+# ifdef VM_MAP
+ pv = vmap(papPages, cPages, VM_MAP, PAGE_KERNEL_RO);
+# else
+ pv = vmap(papPages, cPages, VM_ALLOC, PAGE_KERNEL_RO);
+# endif
+ RTMemFree(papPages);
+ if (pv)
+ {
+ PRTR0MEMOBJLNX pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(sizeof(*pMemLnx), RTR0MEMOBJTYPE_RES_VIRT, pv, cb, pszTag);
+ if (pMemLnx)
+ {
+ pMemLnx->Core.u.ResVirt.R0Process = NIL_RTR0PROCESS;
+ pMemLnx->cPages = 1;
+ pMemLnx->apPages[0] = pDummyPage;
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ vunmap(pv);
+ }
+ }
+ __free_page(pDummyPage);
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+
+#else /* < 2.4.22 */
+ /*
+ * Could probably use ioremap here, but the caller is in a better position than us
+ * to select some safe physical memory.
+ */
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ PRTR0MEMOBJLNX pMemLnx;
+ void *pv;
+ struct task_struct *pTask = rtR0ProcessToLinuxTask(R0Process);
+ if (!pTask)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Check that the specified alignment is supported.
+ */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Let rtR0MemObjLinuxDoMmap do the difficult bits.
+ */
+ pv = rtR0MemObjLinuxDoMmap(R3PtrFixed, cb, uAlignment, pTask, RTMEM_PROT_NONE);
+ if (pv == (void *)-1)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+
+ pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(sizeof(*pMemLnx), RTR0MEMOBJTYPE_RES_VIRT, pv, cb, pszTag);
+ if (!pMemLnx)
+ {
+ rtR0MemObjLinuxDoMunmap(pv, cb, pTask);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+
+ pMemLnx->Core.u.ResVirt.R0Process = R0Process;
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ int rc = VERR_NO_MEMORY;
+ PRTR0MEMOBJLNX pMemLnxToMap = (PRTR0MEMOBJLNX)pMemToMap;
+ PRTR0MEMOBJLNX pMemLnx;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /* Fail if requested to do something we can't. */
+ AssertMsgReturn(pvFixed == (void *)-1, ("%p\n", pvFixed), VERR_NOT_SUPPORTED);
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Create the IPRT memory object.
+ */
+ if (!cbSub)
+ cbSub = pMemLnxToMap->Core.cb - offSub;
+ pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(sizeof(*pMemLnx), RTR0MEMOBJTYPE_MAPPING, NULL, cbSub, pszTag);
+ if (pMemLnx)
+ {
+ if (pMemLnxToMap->cPages)
+ {
+#if RTLNX_VER_MIN(2,4,22)
+ /*
+ * Use vmap - 2.4.22 and later.
+ */
+ pgprot_t fPg = rtR0MemObjLinuxConvertProt(fProt, true /* kernel */);
+ /** @todo We don't really care too much for EXEC here... 5.8 always adds NX. */
+ Assert(((offSub + cbSub) >> PAGE_SHIFT) <= pMemLnxToMap->cPages);
+# ifdef VM_MAP
+ pMemLnx->Core.pv = vmap(&pMemLnxToMap->apPages[offSub >> PAGE_SHIFT], cbSub >> PAGE_SHIFT, VM_MAP, fPg);
+# else
+ pMemLnx->Core.pv = vmap(&pMemLnxToMap->apPages[offSub >> PAGE_SHIFT], cbSub >> PAGE_SHIFT, VM_ALLOC, fPg);
+# endif
+ if (pMemLnx->Core.pv)
+ {
+ pMemLnx->fMappedToRing0 = true;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_MAP_FAILED;
+
+#else /* < 2.4.22 */
+ /*
+ * Only option here is to share mappings if possible and forget about fProt.
+ */
+ if (rtR0MemObjIsRing3(pMemToMap))
+ rc = VERR_NOT_SUPPORTED;
+ else
+ {
+ rc = VINF_SUCCESS;
+ if (!pMemLnxToMap->Core.pv)
+ rc = rtR0MemObjLinuxVMap(pMemLnxToMap, !!(fProt & RTMEM_PROT_EXEC));
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pMemLnxToMap->Core.pv);
+ pMemLnx->Core.pv = (uint8_t *)pMemLnxToMap->Core.pv + offSub;
+ }
+ }
+#endif
+ }
+ else
+ {
+ /*
+ * MMIO / physical memory.
+ */
+ Assert(pMemLnxToMap->Core.enmType == RTR0MEMOBJTYPE_PHYS && !pMemLnxToMap->Core.u.Phys.fAllocated);
+#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.
+ */
+ pMemLnx->Core.pv = pMemLnxToMap->Core.u.Phys.uCachePolicy == RTMEM_CACHE_POLICY_MMIO
+ ? ioremap(pMemLnxToMap->Core.u.Phys.PhysBase + offSub, cbSub)
+ : ioremap_cache(pMemLnxToMap->Core.u.Phys.PhysBase + offSub, cbSub);
+#else /* KERNEL_VERSION < 2.6.25 */
+ pMemLnx->Core.pv = pMemLnxToMap->Core.u.Phys.uCachePolicy == RTMEM_CACHE_POLICY_MMIO
+ ? ioremap_nocache(pMemLnxToMap->Core.u.Phys.PhysBase + offSub, cbSub)
+ : ioremap(pMemLnxToMap->Core.u.Phys.PhysBase + offSub, cbSub);
+#endif /* KERNEL_VERSION < 2.6.25 */
+ if (pMemLnx->Core.pv)
+ {
+ /** @todo fix protection. */
+ rc = VINF_SUCCESS;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ pMemLnx->Core.u.Mapping.R0Process = NIL_RTR0PROCESS;
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemLnx->Core);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+#ifdef VBOX_USE_PAE_HACK
+/**
+ * Replace the PFN of a PTE with the address of the actual page.
+ *
+ * The caller maps a reserved dummy page at the address with the desired access
+ * and flags.
+ *
+ * This hack is required for older Linux kernels which don't provide
+ * remap_pfn_range().
+ *
+ * @returns 0 on success, -ENOMEM on failure.
+ * @param mm The memory context.
+ * @param ulAddr The mapping address.
+ * @param Phys The physical address of the page to map.
+ */
+static int rtR0MemObjLinuxFixPte(struct mm_struct *mm, unsigned long ulAddr, RTHCPHYS Phys)
+{
+ int rc = -ENOMEM;
+ pgd_t *pgd;
+
+ spin_lock(&mm->page_table_lock);
+
+ pgd = pgd_offset(mm, ulAddr);
+ if (!pgd_none(*pgd) && !pgd_bad(*pgd))
+ {
+ pmd_t *pmd = pmd_offset(pgd, ulAddr);
+ if (!pmd_none(*pmd))
+ {
+ pte_t *ptep = pte_offset_map(pmd, ulAddr);
+ if (ptep)
+ {
+ pte_t pte = *ptep;
+ pte.pte_high &= 0xfff00000;
+ pte.pte_high |= ((Phys >> 32) & 0x000fffff);
+ pte.pte_low &= 0x00000fff;
+ pte.pte_low |= (Phys & 0xfffff000);
+ set_pte(ptep, pte);
+ pte_unmap(ptep);
+ rc = 0;
+ }
+ }
+ }
+
+ spin_unlock(&mm->page_table_lock);
+ return rc;
+}
+#endif /* VBOX_USE_PAE_HACK */
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ struct task_struct *pTask = rtR0ProcessToLinuxTask(R0Process);
+ PRTR0MEMOBJLNX pMemLnxToMap = (PRTR0MEMOBJLNX)pMemToMap;
+ int rc = VERR_NO_MEMORY;
+ PRTR0MEMOBJLNX pMemLnx;
+#ifdef VBOX_USE_PAE_HACK
+ struct page *pDummyPage;
+ RTHCPHYS DummyPhys;
+#endif
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Check for restrictions.
+ */
+ if (!pTask)
+ return VERR_NOT_SUPPORTED;
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+#ifdef VBOX_USE_PAE_HACK
+ /*
+ * Allocate a dummy page for use when mapping the memory.
+ */
+ pDummyPage = alloc_page(GFP_USER | __GFP_NOWARN);
+ if (!pDummyPage)
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+ SetPageReserved(pDummyPage);
+ DummyPhys = page_to_phys(pDummyPage);
+#endif
+
+ /*
+ * Create the IPRT memory object.
+ */
+ Assert(!offSub || cbSub);
+ if (cbSub == 0)
+ cbSub = pMemLnxToMap->Core.cb;
+ pMemLnx = (PRTR0MEMOBJLNX)rtR0MemObjNew(sizeof(*pMemLnx), RTR0MEMOBJTYPE_MAPPING, NULL, cbSub, pszTag);
+ if (pMemLnx)
+ {
+ /*
+ * Allocate user space mapping.
+ */
+ void *pv;
+ pv = rtR0MemObjLinuxDoMmap(R3PtrFixed, cbSub, uAlignment, pTask, fProt);
+ if (pv != (void *)-1)
+ {
+ /*
+ * Map page by page into the mmap area.
+ * This is generic, paranoid and not very efficient.
+ */
+ pgprot_t fPg = rtR0MemObjLinuxConvertProt(fProt, false /* user */);
+ unsigned long ulAddrCur = (unsigned long)pv;
+ const size_t cPages = (offSub + cbSub) >> PAGE_SHIFT;
+ size_t iPage;
+
+ LNX_MM_DOWN_WRITE(pTask->mm);
+
+ rc = VINF_SUCCESS;
+ if (pMemLnxToMap->cPages)
+ {
+ for (iPage = offSub >> PAGE_SHIFT; iPage < cPages; iPage++, ulAddrCur += PAGE_SIZE)
+ {
+#if RTLNX_VER_MAX(2,6,11)
+ RTHCPHYS Phys = page_to_phys(pMemLnxToMap->apPages[iPage]);
+#endif
+#if RTLNX_VER_MIN(2,6,0) || defined(HAVE_26_STYLE_REMAP_PAGE_RANGE)
+ struct vm_area_struct *vma = find_vma(pTask->mm, ulAddrCur); /* this is probably the same for all the pages... */
+ AssertBreakStmt(vma, rc = VERR_INTERNAL_ERROR);
+#endif
+#if RTLNX_VER_MAX(2,6,0) && defined(RT_ARCH_X86)
+ /* remap_page_range() limitation on x86 */
+ AssertBreakStmt(Phys < _4G, rc = VERR_NO_MEMORY);
+#endif
+
+#if defined(VBOX_USE_INSERT_PAGE) && RTLNX_VER_MIN(2,6,22)
+ rc = vm_insert_page(vma, ulAddrCur, pMemLnxToMap->apPages[iPage]);
+ /* Thes flags help making 100% sure some bad stuff wont happen (swap, core, ++).
+ * See remap_pfn_range() in mm/memory.c */
+
+#if RTLNX_VER_MIN(6,3,0)
+ vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);
+#elif RTLNX_VER_MIN(3,7,0)
+ vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+#else
+ vma->vm_flags |= VM_RESERVED;
+#endif
+#elif RTLNX_VER_MIN(2,6,11)
+ rc = remap_pfn_range(vma, ulAddrCur, page_to_pfn(pMemLnxToMap->apPages[iPage]), PAGE_SIZE, fPg);
+#elif defined(VBOX_USE_PAE_HACK)
+ rc = remap_page_range(vma, ulAddrCur, DummyPhys, PAGE_SIZE, fPg);
+ if (!rc)
+ rc = rtR0MemObjLinuxFixPte(pTask->mm, ulAddrCur, Phys);
+#elif RTLNX_VER_MIN(2,6,0) || defined(HAVE_26_STYLE_REMAP_PAGE_RANGE)
+ rc = remap_page_range(vma, ulAddrCur, Phys, PAGE_SIZE, fPg);
+#else /* 2.4 */
+ rc = remap_page_range(ulAddrCur, Phys, PAGE_SIZE, fPg);
+#endif
+ if (rc)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+ }
+ else
+ {
+ RTHCPHYS Phys;
+ if (pMemLnxToMap->Core.enmType == RTR0MEMOBJTYPE_PHYS)
+ Phys = pMemLnxToMap->Core.u.Phys.PhysBase;
+ else if (pMemLnxToMap->Core.enmType == RTR0MEMOBJTYPE_CONT)
+ Phys = pMemLnxToMap->Core.u.Cont.Phys;
+ else
+ {
+ AssertMsgFailed(("%d\n", pMemLnxToMap->Core.enmType));
+ Phys = NIL_RTHCPHYS;
+ }
+ if (Phys != NIL_RTHCPHYS)
+ {
+ for (iPage = offSub >> PAGE_SHIFT; iPage < cPages; iPage++, ulAddrCur += PAGE_SIZE, Phys += PAGE_SIZE)
+ {
+#if RTLNX_VER_MIN(2,6,0) || defined(HAVE_26_STYLE_REMAP_PAGE_RANGE)
+ struct vm_area_struct *vma = find_vma(pTask->mm, ulAddrCur); /* this is probably the same for all the pages... */
+ AssertBreakStmt(vma, rc = VERR_INTERNAL_ERROR);
+#endif
+#if RTLNX_VER_MAX(2,6,0) && defined(RT_ARCH_X86)
+ /* remap_page_range() limitation on x86 */
+ AssertBreakStmt(Phys < _4G, rc = VERR_NO_MEMORY);
+#endif
+
+#if RTLNX_VER_MIN(2,6,11)
+ rc = remap_pfn_range(vma, ulAddrCur, Phys, PAGE_SIZE, fPg);
+#elif defined(VBOX_USE_PAE_HACK)
+ rc = remap_page_range(vma, ulAddrCur, DummyPhys, PAGE_SIZE, fPg);
+ if (!rc)
+ rc = rtR0MemObjLinuxFixPte(pTask->mm, ulAddrCur, Phys);
+#elif RTLNX_VER_MIN(2,6,0) || defined(HAVE_26_STYLE_REMAP_PAGE_RANGE)
+ rc = remap_page_range(vma, ulAddrCur, Phys, PAGE_SIZE, fPg);
+#else /* 2.4 */
+ rc = remap_page_range(ulAddrCur, Phys, PAGE_SIZE, fPg);
+#endif
+ if (rc)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef CONFIG_NUMA_BALANCING
+# if RTLNX_VER_MAX(3,13,0) && RTLNX_RHEL_MAX(7,0)
+# define VBOX_NUMA_HACK_OLD
+# endif
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo Ugly hack! But right now we have no other means to
+ * disable automatic NUMA page balancing. */
+# ifdef RT_OS_X86
+# ifdef VBOX_NUMA_HACK_OLD
+ pTask->mm->numa_next_reset = jiffies + 0x7fffffffUL;
+# endif
+ pTask->mm->numa_next_scan = jiffies + 0x7fffffffUL;
+# else
+# ifdef VBOX_NUMA_HACK_OLD
+ pTask->mm->numa_next_reset = jiffies + 0x7fffffffffffffffUL;
+# endif
+ pTask->mm->numa_next_scan = jiffies + 0x7fffffffffffffffUL;
+# endif
+ }
+#endif /* CONFIG_NUMA_BALANCING */
+
+ LNX_MM_UP_WRITE(pTask->mm);
+
+ if (RT_SUCCESS(rc))
+ {
+#ifdef VBOX_USE_PAE_HACK
+ __free_page(pDummyPage);
+#endif
+ pMemLnx->Core.pv = pv;
+ pMemLnx->Core.u.Mapping.R0Process = R0Process;
+ *ppMem = &pMemLnx->Core;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Bail out.
+ */
+ rtR0MemObjLinuxDoMunmap(pv, cbSub, pTask);
+ }
+ rtR0MemObjDelete(&pMemLnx->Core);
+ }
+#ifdef VBOX_USE_PAE_HACK
+ __free_page(pDummyPage);
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+# ifdef IPRT_USE_ALLOC_VM_AREA_FOR_EXEC
+ /*
+ * Currently only supported when we've got addresses PTEs from the kernel.
+ */
+ PRTR0MEMOBJLNX pMemLnx = (PRTR0MEMOBJLNX)pMem;
+ if (pMemLnx->pArea && pMemLnx->papPtesForArea)
+ {
+ pgprot_t const fPg = rtR0MemObjLinuxConvertProt(fProt, true /*fKernel*/);
+ size_t const cPages = (offSub + cbSub) >> PAGE_SHIFT;
+ pte_t **papPtes = pMemLnx->papPtesForArea;
+ size_t i;
+
+ for (i = offSub >> PAGE_SHIFT; i < cPages; i++)
+ {
+ set_pte(papPtes[i], mk_pte(pMemLnx->apPages[i], fPg));
+ }
+ preempt_disable();
+ __flush_tlb_all();
+ preempt_enable();
+ return VINF_SUCCESS;
+ }
+# elif defined(IPRT_USE_APPLY_TO_PAGE_RANGE_FOR_EXEC)
+ PRTR0MEMOBJLNX pMemLnx = (PRTR0MEMOBJLNX)pMem;
+ if ( pMemLnx->fExecutable
+ && pMemLnx->fMappedToRing0)
+ {
+ LNXAPPLYPGRANGE Args;
+ Args.pMemLnx = pMemLnx;
+ Args.fPg = rtR0MemObjLinuxConvertProt(fProt, true /*fKernel*/);
+ int rcLnx = apply_to_page_range(current->active_mm, (unsigned long)pMemLnx->Core.pv + offSub, cbSub,
+ rtR0MemObjLinuxApplyPageRange, (void *)&Args);
+ if (rcLnx)
+ return VERR_NOT_SUPPORTED;
+
+ return VINF_SUCCESS;
+ }
+# endif
+
+ NOREF(pMem);
+ NOREF(offSub);
+ NOREF(cbSub);
+ NOREF(fProt);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ PRTR0MEMOBJLNX pMemLnx = (PRTR0MEMOBJLNX)pMem;
+
+ if (pMemLnx->cPages)
+ return page_to_phys(pMemLnx->apPages[iPage]);
+
+ switch (pMemLnx->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_CONT:
+ return pMemLnx->Core.u.Cont.Phys + (iPage << PAGE_SHIFT);
+
+ case RTR0MEMOBJTYPE_PHYS:
+ return pMemLnx->Core.u.Phys.PhysBase + (iPage << PAGE_SHIFT);
+
+ /* the parent knows */
+ case RTR0MEMOBJTYPE_MAPPING:
+ return rtR0MemObjNativeGetPagePhysAddr(pMemLnx->Core.uRel.Child.pParent, iPage);
+
+ /* cPages > 0 */
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_LOCK:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LARGE_PAGE:
+ default:
+ AssertMsgFailed(("%d\n", pMemLnx->Core.enmType));
+ RT_FALL_THROUGH();
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ return NIL_RTHCPHYS;
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/linux/memuserkernel-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/memuserkernel-r0drv-linux.c
new file mode 100644
index 00000000..e526132d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/memuserkernel-r0drv-linux.c
@@ -0,0 +1,191 @@
+/* $Id: memuserkernel-r0drv-linux.c $ */
+/** @file
+ * IPRT - User & Kernel Memory, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "internal/iprt.h"
+
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+
+
+RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ if (RT_LIKELY(copy_from_user(pvDst, (void *)R3PtrSrc, cb) == 0))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_ACCESS_DENIED;
+}
+RT_EXPORT_SYMBOL(RTR0MemUserCopyFrom);
+
+
+RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ if (RT_LIKELY(copy_to_user((void *)R3PtrDst, pvSrc, cb) == 0))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_ACCESS_DENIED;
+}
+RT_EXPORT_SYMBOL(RTR0MemUserCopyTo);
+
+
+RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+#if RTLNX_VER_MIN(5,0,0) || RTLNX_RHEL_MIN(8,1)
+ bool fRc = access_ok((void *)R3Ptr, 1);
+#else
+ bool fRc = access_ok(VERIFY_READ, (void *)R3Ptr, 1);
+#endif
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return fRc;
+}
+RT_EXPORT_SYMBOL(RTR0MemUserIsValidAddr);
+
+
+RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv)
+{
+ /* Couldn't find a straight forward way of doing this... */
+#if defined(RT_ARCH_X86) && defined(CONFIG_X86_HIGH_ENTRY)
+ return true; /* ?? */
+#elif defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+ return (uintptr_t)pv >= PAGE_OFFSET;
+#else
+# error "PORT ME"
+#if RTLNX_VER_MIN(5,0,0) || RTLNX_RHEL_MIN(8,1)
+ return !access_ok(pv, 1);
+#else
+ return !access_ok(VERIFY_READ, pv, 1);
+#endif
+#endif
+}
+RT_EXPORT_SYMBOL(RTR0MemKernelIsValidAddr);
+
+
+RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void)
+{
+#if defined(RT_ARCH_X86) && defined(CONFIG_X86_HIGH_ENTRY) /* ?? */
+ return false;
+#else
+ return true;
+#endif
+}
+RT_EXPORT_SYMBOL(RTR0MemAreKrnlAndUsrDifferent);
+
+
+/**
+ * Treats both source and destination as unsafe buffers.
+ */
+static int rtR0MemKernelCopyLnxWorker(void *pvDst, void const *pvSrc, size_t cb)
+{
+#if RTLNX_VER_MIN(2,5,55)
+/* _ASM_EXTABLE was introduced in 2.6.25 from what I can tell. Using #ifndef
+ here since it has to be a macro and you never know what someone might have
+ backported to an earlier kernel release. */
+# ifndef _ASM_EXTABLE
+# if ARCH_BITS == 32
+# define _ASM_EXTABLE(a_Instr, a_Resume) \
+ ".section __ex_table,\"a\"\n" \
+ ".balign 4\n" \
+ ".long " #a_Instr "\n" \
+ ".long " #a_Resume "\n" \
+ ".previous\n"
+# else
+# define _ASM_EXTABLE(a_Instr, a_Resume) \
+ ".section __ex_table,\"a\"\n" \
+ ".balign 8\n" \
+ ".quad " #a_Instr "\n" \
+ ".quad " #a_Resume "\n" \
+ ".previous\n"
+# endif
+# endif /* !_ASM_EXTABLE */
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC(); /* paranoia */
+ if (!cb)
+ return VINF_SUCCESS;
+
+ __asm__ __volatile__ ("cld\n"
+ "1:\n\t"
+ "rep; movsb\n"
+ "2:\n\t"
+ ".section .fixup,\"ax\"\n"
+ "3:\n\t"
+ "movl %4, %0\n\t"
+ "jmp 2b\n\t"
+ ".previous\n"
+ _ASM_EXTABLE(1b, 3b)
+ : "=r" (rc),
+ "=D" (pvDst),
+ "=S" (pvSrc),
+ "=c" (cb)
+ : "i" (VERR_ACCESS_DENIED),
+ "0" (VINF_SUCCESS),
+ "1" (pvDst),
+ "2" (pvSrc),
+ "3" (cb)
+ : "memory");
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+#else
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb)
+{
+ return rtR0MemKernelCopyLnxWorker(pvDst, pvSrc, cb);
+}
+RT_EXPORT_SYMBOL(RTR0MemKernelCopyFrom);
+
+
+RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb)
+{
+ return rtR0MemKernelCopyLnxWorker(pvDst, pvSrc, cb);
+}
+RT_EXPORT_SYMBOL(RTR0MemKernelCopyTo);
+
diff --git a/src/VBox/Runtime/r0drv/linux/mp-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/mp-r0drv-linux.c
new file mode 100644
index 00000000..5ac05db5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/mp-r0drv-linux.c
@@ -0,0 +1,640 @@
+/* $Id: mp-r0drv-linux.c $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/thread.h>
+#include "r0drv/mp-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if defined(nr_cpumask_bits) || RTLNX_VER_MIN(2,6,28)
+# define VBOX_NR_CPUMASK_BITS (nr_cpumask_bits) /* same as nr_cpu_ids */
+#else
+# define VBOX_NR_CPUMASK_BITS (NR_CPUS)
+#endif
+
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ return smp_processor_id();
+}
+RT_EXPORT_SYMBOL(RTMpCpuId);
+
+
+RTDECL(int) RTMpCurSetIndex(void)
+{
+ return smp_processor_id();
+}
+RT_EXPORT_SYMBOL(RTMpCurSetIndex);
+
+
+RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu)
+{
+ return *pidCpu = smp_processor_id();
+}
+RT_EXPORT_SYMBOL(RTMpCurSetIndexAndId);
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS && idCpu < VBOX_NR_CPUMASK_BITS ? (int)idCpu : -1;
+}
+RT_EXPORT_SYMBOL(RTMpCpuIdToSetIndex);
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu < VBOX_NR_CPUMASK_BITS ? (RTCPUID)iCpu : NIL_RTCPUID;
+}
+RT_EXPORT_SYMBOL(RTMpCpuIdFromSetIndex);
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return VBOX_NR_CPUMASK_BITS - 1;
+}
+RT_EXPORT_SYMBOL(RTMpGetMaxCpuId);
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+#if defined(CONFIG_SMP)
+# if RTLNX_VER_MIN(2,6,2) || defined(cpu_possible)
+ return idCpu < VBOX_NR_CPUMASK_BITS && cpu_possible(idCpu);
+# else /* < 2.5.29 */
+ return idCpu < (RTCPUID)(smp_num_cpus);
+# endif
+#else
+ return idCpu == RTMpCpuId();
+#endif
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuPossible);
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+ return pSet;
+}
+RT_EXPORT_SYMBOL(RTMpGetSet);
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+#ifdef CONFIG_SMP
+# if RTLNX_VER_MIN(2,6,4) || defined(num_possible_cpus)
+ return num_possible_cpus();
+# elif RTLNX_VER_MAX(2,5,0)
+ return smp_num_cpus;
+# else
+ RTCPUSET Set;
+ RTMpGetSet(&Set);
+ return RTCpuSetCount(&Set);
+# endif
+#else
+ return 1;
+#endif
+}
+RT_EXPORT_SYMBOL(RTMpGetCount);
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+#ifdef CONFIG_SMP
+# if RTLNX_VER_MIN(2,6,0) || defined(cpu_online)
+ return idCpu < VBOX_NR_CPUMASK_BITS && cpu_online(idCpu);
+# else /* 2.4: */
+ return idCpu < VBOX_NR_CPUMASK_BITS && cpu_online_map & RT_BIT_64(idCpu);
+# endif
+#else
+ return idCpu == RTMpCpuId();
+#endif
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuOnline);
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+#ifdef CONFIG_SMP
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId();
+ do
+ {
+ if (RTMpIsCpuOnline(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+#else
+ RTCpuSetEmpty(pSet);
+ RTCpuSetAdd(pSet, RTMpCpuId());
+#endif
+ return pSet;
+}
+RT_EXPORT_SYMBOL(RTMpGetOnlineSet);
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+#ifdef CONFIG_SMP
+# if RTLNX_VER_MIN(2,6,0) || defined(num_online_cpus)
+ return num_online_cpus();
+# else
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+# endif
+#else
+ return 1;
+#endif
+}
+RT_EXPORT_SYMBOL(RTMpGetOnlineCount);
+
+
+RTDECL(bool) RTMpIsCpuWorkPending(void)
+{
+ /** @todo (not used on non-Windows platforms yet). */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTMpIsCpuWorkPending);
+
+
+/**
+ * Wrapper between the native linux per-cpu callbacks and PFNRTWORKER.
+ *
+ * @param pvInfo Pointer to the RTMPARGS package.
+ */
+static void rtmpLinuxWrapper(void *pvInfo)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
+ ASMAtomicIncU32(&pArgs->cHits);
+ pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2);
+}
+
+#ifdef CONFIG_SMP
+
+# if RTLNX_VER_MIN(2,6,27)
+/**
+ * Wrapper between the native linux per-cpu callbacks and PFNRTWORKER, does hit
+ * increment after calling the worker.
+ *
+ * @param pvInfo Pointer to the RTMPARGS package.
+ */
+static void rtmpLinuxWrapperPostInc(void *pvInfo)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
+ pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+}
+# endif
+
+
+/**
+ * Wrapper between the native linux all-cpu callbacks and PFNRTWORKER.
+ *
+ * @param pvInfo Pointer to the RTMPARGS package.
+ */
+static void rtmpLinuxAllWrapper(void *pvInfo)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
+ PRTCPUSET pWorkerSet = pArgs->pWorkerSet;
+ RTCPUID idCpu = RTMpCpuId();
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+
+ if (RTCpuSetIsMember(pWorkerSet, idCpu))
+ {
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ RTCpuSetDel(pWorkerSet, idCpu);
+ }
+}
+
+#endif /* CONFIG_SMP */
+
+RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ RTMPARGS Args;
+ RTCPUSET OnlineSet;
+ RTCPUID idCpu;
+#ifdef CONFIG_SMP
+ uint32_t cLoops;
+#endif
+
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = NIL_RTCPUID;
+ Args.cHits = 0;
+
+ RTThreadPreemptDisable(&PreemptState);
+ RTMpGetOnlineSet(&OnlineSet);
+ Args.pWorkerSet = &OnlineSet;
+ idCpu = RTMpCpuId();
+
+#ifdef CONFIG_SMP
+ if (RTCpuSetCount(&OnlineSet) > 1)
+ {
+ /* Fire the function on all other CPUs without waiting for completion. */
+# if RTLNX_VER_MIN(5,3,0)
+ smp_call_function(rtmpLinuxAllWrapper, &Args, 0 /* wait */);
+# elif RTLNX_VER_MIN(2,6,27)
+ int rc = smp_call_function(rtmpLinuxAllWrapper, &Args, 0 /* wait */);
+ Assert(!rc); NOREF(rc);
+# else
+ int rc = smp_call_function(rtmpLinuxAllWrapper, &Args, 0 /* retry */, 0 /* wait */);
+ Assert(!rc); NOREF(rc);
+# endif
+ }
+#endif
+
+ /* Fire the function on this CPU. */
+ Args.pfnWorker(idCpu, Args.pvUser1, Args.pvUser2);
+ RTCpuSetDel(Args.pWorkerSet, idCpu);
+
+#ifdef CONFIG_SMP
+ /* Wait for all of them finish. */
+ cLoops = 64000;
+ while (!RTCpuSetIsEmpty(Args.pWorkerSet))
+ {
+ /* Periodically check if any CPU in the wait set has gone offline, if so update the wait set. */
+ if (!cLoops--)
+ {
+ RTCPUSET OnlineSetNow;
+ RTMpGetOnlineSet(&OnlineSetNow);
+ RTCpuSetAnd(Args.pWorkerSet, &OnlineSetNow);
+
+ cLoops = 64000;
+ }
+
+ ASMNopPause();
+ }
+#endif
+
+ RTThreadPreemptRestore(&PreemptState);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpOnAll);
+
+
+RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+#ifdef CONFIG_SMP
+ IPRT_LINUX_SAVE_EFL_AC();
+ RTMPARGS Args;
+
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = NIL_RTCPUID;
+ Args.cHits = 0;
+
+ RTThreadPreemptDisable(&PreemptState);
+# if RTLNX_VER_MIN(5,3,0)
+ smp_call_function(rtmpLinuxWrapper, &Args, 1 /* wait */);
+# elif RTLNX_VER_MIN(2,6,27)
+ int rc = smp_call_function(rtmpLinuxWrapper, &Args, 1 /* wait */);
+ Assert(rc == 0); NOREF(rc);
+# else /* older kernels */
+ int rc = smp_call_function(rtmpLinuxWrapper, &Args, 0 /* retry */, 1 /* wait */);
+ Assert(rc == 0); NOREF(rc);
+# endif /* older kernels */
+ RTThreadPreemptRestore(&PreemptState);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+#else
+ RT_NOREF(pfnWorker, pvUser1, pvUser2);
+#endif
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpOnOthers);
+
+
+#if RTLNX_VER_MAX(2,6,27) && defined(CONFIG_SMP)
+/**
+ * Wrapper between the native linux per-cpu callbacks and PFNRTWORKER
+ * employed by RTMpOnPair on older kernels that lacks smp_call_function_many.
+ *
+ * @param pvInfo Pointer to the RTMPARGS package.
+ */
+static void rtMpLinuxOnPairWrapper(void *pvInfo)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
+ RTCPUID idCpu = RTMpCpuId();
+
+ if ( idCpu == pArgs->idCpu
+ || idCpu == pArgs->idCpu2)
+ {
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+ }
+}
+#endif
+
+
+RTDECL(int) RTMpOnPair(RTCPUID idCpu1, RTCPUID idCpu2, uint32_t fFlags, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+#ifdef CONFIG_SMP
+ IPRT_LINUX_SAVE_EFL_AC();
+ int rc;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+# if RTLNX_VER_MIN(2,6,28) /* 2.6.28 introduces CONFIG_CPUMASK_OFFSTACK */
+ cpumask_var_t DstCpuMask;
+# elif RTLNX_VER_MIN(2,6,27)
+ cpumask_t DstCpuMask;
+# endif
+
+ AssertReturn(idCpu1 != idCpu2, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & RTMPON_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ /*
+ * Prepare the CPU mask before we disable preemption.
+ */
+# if RTLNX_VER_MIN(2,6,30)
+ if (!zalloc_cpumask_var(&DstCpuMask, GFP_KERNEL))
+ return VERR_NO_MEMORY;
+ cpumask_set_cpu(idCpu1, DstCpuMask);
+ cpumask_set_cpu(idCpu2, DstCpuMask);
+# elif RTLNX_VER_MIN(2,6,28)
+ if (!alloc_cpumask_var(&DstCpuMask, GFP_KERNEL))
+ return VERR_NO_MEMORY;
+ cpumask_clear(DstCpuMask);
+ cpumask_set_cpu(idCpu1, DstCpuMask);
+ cpumask_set_cpu(idCpu2, DstCpuMask);
+# elif RTLNX_VER_MIN(2,6,27)
+ cpus_clear(DstCpuMask);
+ cpu_set(idCpu1, DstCpuMask);
+ cpu_set(idCpu2, DstCpuMask);
+# endif
+
+ /*
+ * Check that both CPUs are online before doing the broadcast call.
+ */
+ RTThreadPreemptDisable(&PreemptState);
+ if ( RTMpIsCpuOnline(idCpu1)
+ && RTMpIsCpuOnline(idCpu2))
+ {
+ /*
+ * Use the smp_call_function variant taking a cpu mask where available,
+ * falling back on broadcast with filter. Slight snag if one of the
+ * CPUs is the one we're running on, we must do the call and the post
+ * call wait ourselves.
+ */
+ RTCPUID idCpuSelf = RTMpCpuId();
+ bool const fCallSelf = idCpuSelf == idCpu1 || idCpuSelf == idCpu2;
+ RTMPARGS Args;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu1;
+ Args.idCpu2 = idCpu2;
+ Args.cHits = 0;
+
+# if RTLNX_VER_MIN(2,6,28)
+ smp_call_function_many(DstCpuMask, rtmpLinuxWrapperPostInc, &Args, !fCallSelf /* wait */);
+ rc = 0;
+# elif RTLNX_VER_MIN(2,6,27)
+ rc = smp_call_function_mask(DstCpuMask, rtmpLinuxWrapperPostInc, &Args, !fCallSelf /* wait */);
+# else /* older kernels */
+ rc = smp_call_function(rtMpLinuxOnPairWrapper, &Args, 0 /* retry */, !fCallSelf /* wait */);
+# endif /* older kernels */
+ Assert(rc == 0);
+
+ /* Call ourselves if necessary and wait for the other party to be done. */
+ if (fCallSelf)
+ {
+ uint32_t cLoops = 0;
+ rtmpLinuxWrapper(&Args);
+ while (ASMAtomicReadU32(&Args.cHits) < 2)
+ {
+ if ((cLoops & 0x1ff) == 0 && !RTMpIsCpuOnline(idCpuSelf == idCpu1 ? idCpu2 : idCpu1))
+ break;
+ cLoops++;
+ ASMNopPause();
+ }
+ }
+
+ Assert(Args.cHits <= 2);
+ if (Args.cHits == 2)
+ rc = VINF_SUCCESS;
+ else if (Args.cHits == 1)
+ rc = VERR_NOT_ALL_CPUS_SHOWED;
+ else if (Args.cHits == 0)
+ rc = VERR_CPU_OFFLINE;
+ else
+ rc = VERR_CPU_IPE_1;
+ }
+ /*
+ * A CPU must be present to be considered just offline.
+ */
+ else if ( RTMpIsCpuPresent(idCpu1)
+ && RTMpIsCpuPresent(idCpu2))
+ rc = VERR_CPU_OFFLINE;
+ else
+ rc = VERR_CPU_NOT_FOUND;
+
+ RTThreadPreemptRestore(&PreemptState);;
+# if RTLNX_VER_MIN(2,6,28)
+ free_cpumask_var(DstCpuMask);
+# endif
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+
+#else /* !CONFIG_SMP */
+ RT_NOREF(idCpu1, idCpu2, fFlags, pfnWorker, pvUser1, pvUser2);
+ return VERR_CPU_NOT_FOUND;
+#endif /* !CONFIG_SMP */
+}
+RT_EXPORT_SYMBOL(RTMpOnPair);
+
+
+RTDECL(bool) RTMpOnPairIsConcurrentExecSupported(void)
+{
+ return true;
+}
+RT_EXPORT_SYMBOL(RTMpOnPairIsConcurrentExecSupported);
+
+
+#if RTLNX_VER_MAX(2,6,19) && defined(CONFIG_SMP)
+/**
+ * Wrapper between the native linux per-cpu callbacks and PFNRTWORKER
+ * employed by RTMpOnSpecific on older kernels that lacks smp_call_function_single.
+ *
+ * @param pvInfo Pointer to the RTMPARGS package.
+ */
+static void rtmpOnSpecificLinuxWrapper(void *pvInfo)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)pvInfo;
+ RTCPUID idCpu = RTMpCpuId();
+
+ if (idCpu == pArgs->idCpu)
+ {
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+ }
+}
+#endif
+
+
+RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ int rc;
+ RTMPARGS Args;
+
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu;
+ Args.cHits = 0;
+
+ if (!RTMpIsCpuPossible(idCpu))
+ return VERR_CPU_NOT_FOUND;
+
+ RTThreadPreemptDisable(&PreemptState);
+ if (idCpu != RTMpCpuId())
+ {
+#ifdef CONFIG_SMP
+ if (RTMpIsCpuOnline(idCpu))
+ {
+# if RTLNX_VER_MIN(2,6,27)
+ rc = smp_call_function_single(idCpu, rtmpLinuxWrapper, &Args, 1 /* wait */);
+# elif RTLNX_VER_MIN(2,6,19)
+ rc = smp_call_function_single(idCpu, rtmpLinuxWrapper, &Args, 0 /* retry */, 1 /* wait */);
+# else /* older kernels */
+ rc = smp_call_function(rtmpOnSpecificLinuxWrapper, &Args, 0 /* retry */, 1 /* wait */);
+# endif /* older kernels */
+ Assert(rc == 0);
+ rc = Args.cHits ? VINF_SUCCESS : VERR_CPU_OFFLINE;
+ }
+ else
+#endif /* CONFIG_SMP */
+ rc = VERR_CPU_OFFLINE;
+ }
+ else
+ {
+ rtmpLinuxWrapper(&Args);
+ rc = VINF_SUCCESS;
+ }
+ RTThreadPreemptRestore(&PreemptState);;
+
+ NOREF(rc);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTMpOnSpecific);
+
+
+#if RTLNX_VER_MIN(2,6,19) && defined(CONFIG_SMP)
+/**
+ * Dummy callback used by RTMpPokeCpu.
+ *
+ * @param pvInfo Ignored.
+ */
+static void rtmpLinuxPokeCpuCallback(void *pvInfo)
+{
+ NOREF(pvInfo);
+}
+#endif
+
+
+RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
+{
+#if RTLNX_VER_MIN(2,6,19)
+ IPRT_LINUX_SAVE_EFL_AC();
+ int rc;
+ if (RTMpIsCpuPossible(idCpu))
+ {
+ if (RTMpIsCpuOnline(idCpu))
+ {
+# ifdef CONFIG_SMP
+# if RTLNX_VER_MIN(2,6,27)
+ rc = smp_call_function_single(idCpu, rtmpLinuxPokeCpuCallback, NULL, 0 /* wait */);
+# elif RTLNX_VER_MIN(2,6,19)
+ rc = smp_call_function_single(idCpu, rtmpLinuxPokeCpuCallback, NULL, 0 /* retry */, 0 /* wait */);
+# else /* older kernels */
+# error oops
+# endif /* older kernels */
+ Assert(rc == 0);
+# endif /* CONFIG_SMP */
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_CPU_OFFLINE;
+ }
+ else
+ rc = VERR_CPU_NOT_FOUND;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+
+#else /* older kernels */
+ /* no unicast here? */
+ return VERR_NOT_SUPPORTED;
+#endif /* older kernels */
+}
+RT_EXPORT_SYMBOL(RTMpPokeCpu);
+
+
+RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
+{
+ return true;
+}
+RT_EXPORT_SYMBOL(RTMpOnAllIsConcurrentSafe);
+
diff --git a/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c
new file mode 100644
index 00000000..99b8748e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/mpnotification-r0drv-linux.c
@@ -0,0 +1,258 @@
+/* $Id: mpnotification-r0drv-linux.c $ */
+/** @file
+ * IPRT - Multiprocessor Event Notifications, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/errcore.h>
+#include <iprt/cpuset.h>
+#include <iprt/thread.h>
+#include "r0drv/mp-r0drv.h"
+
+#if RTLNX_VER_MIN(4,10,0)
+
+static enum cpuhp_state g_rtR0MpOnline;
+
+/*
+ * Linux 4.10 completely removed CPU notifiers. So let's switch to CPU hotplug
+ * notification.
+ */
+
+static int rtR0MpNotificationLinuxOnline(unsigned int cpu)
+{
+ RTCPUID idCpu = RTMpCpuIdFromSetIndex(cpu);
+ rtMpNotificationDoCallbacks(RTMPEVENT_ONLINE, idCpu);
+ return 0;
+}
+
+static int rtR0MpNotificationLinuxOffline(unsigned int cpu)
+{
+ RTCPUID idCpu = RTMpCpuIdFromSetIndex(cpu);
+ rtMpNotificationDoCallbacks(RTMPEVENT_OFFLINE, idCpu);
+ return 0;
+}
+
+DECLHIDDEN(int) rtR0MpNotificationNativeInit(void)
+{
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC();
+ rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "vboxdrv:online",
+ rtR0MpNotificationLinuxOnline, rtR0MpNotificationLinuxOffline);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ /*
+ * cpuhp_setup_state_nocalls() returns a positive state number for
+ * CPUHP_AP_ONLINE_DYN or -ENOSPC if there is no free slot available
+ * (see cpuhp_reserve_state / definition of CPUHP_AP_ONLINE_DYN).
+ */
+ AssertMsgReturn(rc > 0, ("%d\n", rc), RTErrConvertFromErrno(rc));
+ g_rtR0MpOnline = rc;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ cpuhp_remove_state_nocalls(g_rtR0MpOnline);
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+#elif RTLNX_VER_MIN(2,5,71) && defined(CONFIG_SMP)
+
+static int rtMpNotificationLinuxCallback(struct notifier_block *pNotifierBlock, unsigned long ulNativeEvent, void *pvCpu);
+
+/**
+ * The notifier block we use for registering the callback.
+ */
+static struct notifier_block g_NotifierBlock =
+{
+ .notifier_call = rtMpNotificationLinuxCallback,
+ .next = NULL,
+ .priority = 0
+};
+
+# ifdef CPU_DOWN_FAILED
+/**
+ * The set of CPUs we've seen going offline recently.
+ */
+static RTCPUSET g_MpPendingOfflineSet;
+# endif
+
+
+/**
+ * The native callback.
+ *
+ * @returns NOTIFY_DONE.
+ * @param pNotifierBlock Pointer to g_NotifierBlock.
+ * @param ulNativeEvent The native event.
+ * @param pvCpu The cpu id cast into a pointer value.
+ *
+ * @remarks This can fire with preemption enabled and on any CPU.
+ */
+static int rtMpNotificationLinuxCallback(struct notifier_block *pNotifierBlock, unsigned long ulNativeEvent, void *pvCpu)
+{
+ bool fProcessEvent = false;
+ RTCPUID idCpu = (uintptr_t)pvCpu;
+ NOREF(pNotifierBlock);
+
+ /*
+ * Note that redhat/CentOS ported _some_ of the FROZEN macros
+ * back to their 2.6.18-92.1.10.el5 kernel but actually don't
+ * use them. Thus we have to test for both CPU_TASKS_FROZEN and
+ * the individual event variants.
+ */
+ switch (ulNativeEvent)
+ {
+ /*
+ * Pick up online events or failures to go offline.
+ * Ignore failure events for CPUs we didn't see go offline.
+ */
+# ifdef CPU_DOWN_FAILED
+ case CPU_DOWN_FAILED:
+# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_FAILED_FROZEN)
+ case CPU_DOWN_FAILED_FROZEN:
+# endif
+ if (!RTCpuSetIsMember(&g_MpPendingOfflineSet, idCpu))
+ break; /* fProcessEvents = false */
+ /* fall thru */
+# endif
+ case CPU_ONLINE:
+# if defined(CPU_TASKS_FROZEN) && defined(CPU_ONLINE_FROZEN)
+ case CPU_ONLINE_FROZEN:
+# endif
+# ifdef CPU_DOWN_FAILED
+ RTCpuSetDel(&g_MpPendingOfflineSet, idCpu);
+# endif
+ fProcessEvent = true;
+ break;
+
+ /*
+ * Pick the earliest possible offline event.
+ * The only important thing here is that we get the event and that
+ * it's exactly one.
+ */
+# ifdef CPU_DOWN_PREPARE
+ case CPU_DOWN_PREPARE:
+# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_PREPARE_FROZEN)
+ case CPU_DOWN_PREPARE_FROZEN:
+# endif
+ fProcessEvent = true;
+# else
+ case CPU_DEAD:
+# if defined(CPU_TASKS_FROZEN) && defined(CPU_DEAD_FROZEN)
+ case CPU_DEAD_FROZEN:
+# endif
+ /* Don't process CPU_DEAD notifications. */
+# endif
+# ifdef CPU_DOWN_FAILED
+ RTCpuSetAdd(&g_MpPendingOfflineSet, idCpu);
+# endif
+ break;
+ }
+
+ if (!fProcessEvent)
+ return NOTIFY_DONE;
+
+ switch (ulNativeEvent)
+ {
+# ifdef CPU_DOWN_FAILED
+ case CPU_DOWN_FAILED:
+# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_FAILED_FROZEN)
+ case CPU_DOWN_FAILED_FROZEN:
+# endif
+# endif
+ case CPU_ONLINE:
+# if defined(CPU_TASKS_FROZEN) && defined(CPU_ONLINE_FROZEN)
+ case CPU_ONLINE_FROZEN:
+# endif
+ rtMpNotificationDoCallbacks(RTMPEVENT_ONLINE, idCpu);
+ break;
+
+# ifdef CPU_DOWN_PREPARE
+ case CPU_DOWN_PREPARE:
+# if defined(CPU_TASKS_FROZEN) && defined(CPU_DOWN_PREPARE_FROZEN)
+ case CPU_DOWN_PREPARE_FROZEN:
+# endif
+ rtMpNotificationDoCallbacks(RTMPEVENT_OFFLINE, idCpu);
+ break;
+# endif
+ }
+
+ return NOTIFY_DONE;
+}
+
+
+DECLHIDDEN(int) rtR0MpNotificationNativeInit(void)
+{
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+# ifdef CPU_DOWN_FAILED
+ RTCpuSetEmpty(&g_MpPendingOfflineSet);
+# endif
+
+ rc = register_cpu_notifier(&g_NotifierBlock);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ AssertMsgReturn(!rc, ("%d\n", rc), RTErrConvertFromErrno(rc));
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ unregister_cpu_notifier(&g_NotifierBlock);
+ IPRT_LINUX_RESTORE_EFL_AC();
+}
+
+#else /* Not supported / Not needed */
+
+DECLHIDDEN(int) rtR0MpNotificationNativeInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void)
+{
+}
+
+#endif /* Not supported / Not needed */
+
diff --git a/src/VBox/Runtime/r0drv/linux/process-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/process-r0drv-linux.c
new file mode 100644
index 00000000..3179a95e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/process-r0drv-linux.c
@@ -0,0 +1,59 @@
+/* $Id: process-r0drv-linux.c $ */
+/** @file
+ * IPRT - Process, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/process.h>
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ return (RTPROCESS)current->tgid;
+}
+RT_EXPORT_SYMBOL(RTProcSelf);
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ return (RTR0PROCESS)current->tgid;
+}
+RT_EXPORT_SYMBOL(RTR0ProcHandleSelf);
+
diff --git a/src/VBox/Runtime/r0drv/linux/rtStrFormatKernelAddress-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/rtStrFormatKernelAddress-r0drv-linux.c
new file mode 100644
index 00000000..14cfc4f7
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/rtStrFormatKernelAddress-r0drv-linux.c
@@ -0,0 +1,66 @@
+/* $Id: rtStrFormatKernelAddress-r0drv-linux.c $ */
+/** @file
+ * IPRT - IPRT String Formatter, ring-0 addresses.
+ */
+
+/*
+ * 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 RTLOGGROUP_STRING
+#include "the-linux-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include "internal/string.h"
+
+
+DECLHIDDEN(size_t) rtStrFormatKernelAddress(char *pszBuf, size_t cbBuf, RTR0INTPTR uPtr, signed int cchWidth,
+ signed int cchPrecision, unsigned int fFlags)
+{
+#if !defined(DEBUG) && RTLNX_VER_MIN(2,6,38)
+ RT_NOREF(cchWidth, cchPrecision);
+ /* use the Linux kernel function which is able to handle "%pK" */
+ static const char s_szFmt[] = "0x%pK";
+ const char *pszFmt = s_szFmt;
+ if (!(fFlags & RTSTR_F_SPECIAL))
+ pszFmt += 2;
+ return scnprintf(pszBuf, cbBuf, pszFmt, uPtr);
+#else
+ Assert(cbBuf >= 64);
+ return RTStrFormatNumber(pszBuf, uPtr, 16, cchWidth, cchPrecision, fFlags);
+#endif
+}
diff --git a/src/VBox/Runtime/r0drv/linux/semevent-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/semevent-r0drv-linux.c
new file mode 100644
index 00000000..0b1ca618
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/semevent-r0drv-linux.c
@@ -0,0 +1,296 @@
+/* $Id: semevent-r0drv-linux.c $ */
+/** @file
+ * IPRT - Single Release Event Semaphores, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 RTSEMEVENT_WITHOUT_REMAPPING
+#include "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+
+#include "waitqueue-r0drv-linux.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Linux event semaphore.
+ */
+typedef struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The object status - !0 when signaled and 0 when reset. */
+ uint32_t volatile fState;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The wait queue. */
+ wait_queue_head_t Head;
+} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ PRTSEMEVENTINTERNAL pThis;
+ IPRT_LINUX_SAVE_EFL_AC();
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+
+ pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->fState = 0;
+ pThis->cRefs = 1;
+ init_waitqueue_head(&pThis->Head);
+
+ *phEventSem = pThis;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemEventCreate);
+
+
+/**
+ * Retains a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventLnxRetain(PRTSEMEVENTINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < 100000); NOREF(cRefs);
+}
+
+
+/**
+ * Releases a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventLnxRelease(PRTSEMEVENTINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ RTMemFree(pThis);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC);
+ ASMAtomicWriteU32(&pThis->fState, 0);
+ Assert(!waitqueue_active(&pThis->Head));
+ wake_up_all(&pThis->Head);
+ rtR0SemEventLnxRelease(pThis);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemEventDestroy);
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ rtR0SemEventLnxRetain(pThis);
+
+ /*
+ * Signal the event object.
+ */
+ ASMAtomicWriteU32(&pThis->fState, 1);
+ wake_up(&pThis->Head);
+
+ rtR0SemEventLnxRelease(pThis);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemEventSignal);
+
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventLnxWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ int rc;
+ RT_NOREF_PV(pSrcPos);
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ rtR0SemEventLnxRetain(pThis);
+
+ /*
+ * Try grab the event without setting up the wait.
+ */
+ if ( 1 /** @todo check if there are someone waiting already - waitqueue_active, but then what do we do below? */
+ && ASMAtomicCmpXchgU32(&pThis->fState, 0, 1))
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ IPRT_LINUX_SAVE_EFL_AC();
+ RTR0SEMLNXWAIT Wait;
+ rc = rtR0SemLnxWaitInit(&Wait, fFlags, uTimeout, &pThis->Head);
+ if (RT_SUCCESS(rc))
+ {
+ IPRT_DEBUG_SEMS_STATE(pThis, 'E');
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ rtR0SemLnxWaitPrepare(&Wait);
+
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1))
+ rc = VINF_SUCCESS;
+ else if (rtR0SemLnxWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemLnxWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemLnxWaitDoIt(&Wait);
+ continue;
+ }
+ }
+ break;
+ }
+
+ rtR0SemLnxWaitDelete(&Wait);
+ IPRT_DEBUG_SEMS_STATE_RC(pThis, 'E', rc);
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+
+ rtR0SemEventLnxRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventLnxWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventLnxWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitEx);
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventLnxWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ return rtR0SemLnxWaitGetResolution();
+}
+RT_EXPORT_SYMBOL(RTSemEventGetResolution);
+
+
+RTR0DECL(bool) RTSemEventIsSignalSafe(void)
+{
+ return true;
+}
+RT_EXPORT_SYMBOL(RTSemEventIsSignalSafe);
+
diff --git a/src/VBox/Runtime/r0drv/linux/semeventmulti-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/semeventmulti-r0drv-linux.c
new file mode 100644
index 00000000..fbc028a1
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/semeventmulti-r0drv-linux.c
@@ -0,0 +1,361 @@
+/* $Id: semeventmulti-r0drv-linux.c $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#include "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/lockvalidator.h>
+
+#include "waitqueue-r0drv-linux.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name fStateAndGen values
+ * @{ */
+/** The state bit number. */
+#define RTSEMEVENTMULTILNX_STATE_BIT 0
+/** The state mask. */
+#define RTSEMEVENTMULTILNX_STATE_MASK RT_BIT_32(RTSEMEVENTMULTILNX_STATE_BIT)
+/** The generation mask. */
+#define RTSEMEVENTMULTILNX_GEN_MASK ~RTSEMEVENTMULTILNX_STATE_MASK
+/** The generation shift. */
+#define RTSEMEVENTMULTILNX_GEN_SHIFT 1
+/** The initial variable value. */
+#define RTSEMEVENTMULTILNX_STATE_GEN_INIT UINT32_C(0xfffffffc)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Linux event semaphore.
+ */
+typedef struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The object state bit and generation counter.
+ * The generation counter is incremented every time the object is
+ * signalled. */
+ uint32_t volatile fStateAndGen;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The wait queue. */
+ wait_queue_head_t Head;
+} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
+
+
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis;
+ IPRT_LINUX_SAVE_EFL_AC();
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->fStateAndGen = RTSEMEVENTMULTILNX_STATE_GEN_INIT;
+ pThis->cRefs = 1;
+ init_waitqueue_head(&pThis->Head);
+
+ *phEventMultiSem = pThis;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiCreate);
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiLnxRetain(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ NOREF(cRefs);
+ Assert(cRefs && cRefs < 100000);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiLnxRelease(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ {
+ Assert(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC);
+ RTMemFree(pThis);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC);
+ ASMAtomicAndU32(&pThis->fStateAndGen, RTSEMEVENTMULTILNX_GEN_MASK);
+ Assert(!waitqueue_active(&pThis->Head));
+ wake_up_all(&pThis->Head);
+ rtR0SemEventMultiLnxRelease(pThis);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiDestroy);
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ uint32_t fNew;
+ uint32_t fOld;
+
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiLnxRetain(pThis);
+
+ /*
+ * Signal the event object. The cause of the paranoia here is racing to try
+ * deal with racing RTSemEventMultiSignal calls (should probably be
+ * forbidden, but it's relatively easy to handle).
+ */
+ do
+ {
+ fNew = fOld = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ fNew += 1 << RTSEMEVENTMULTILNX_GEN_SHIFT;
+ fNew |= RTSEMEVENTMULTILNX_STATE_MASK;
+ }
+ while (!ASMAtomicCmpXchgU32(&pThis->fStateAndGen, fNew, fOld));
+
+ wake_up_all(&pThis->Head);
+
+ rtR0SemEventMultiLnxRelease(pThis);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiSignal);
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiLnxRetain(pThis);
+
+ /*
+ * Reset it.
+ */
+ ASMAtomicAndU32(&pThis->fStateAndGen, ~RTSEMEVENTMULTILNX_STATE_MASK);
+
+ rtR0SemEventMultiLnxRelease(pThis);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiReset);
+
+
+/**
+ * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param uTimeout See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventMultiLnxWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ uint32_t fOrgStateAndGen;
+ int rc;
+ RT_NOREF_PV(pSrcPos);
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiLnxRetain(pThis);
+
+ /*
+ * Is the event already signalled or do we have to wait?
+ */
+ fOrgStateAndGen = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ if (fOrgStateAndGen & RTSEMEVENTMULTILNX_STATE_MASK)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ RTR0SEMLNXWAIT Wait;
+ IPRT_LINUX_SAVE_EFL_AC();
+ rc = rtR0SemLnxWaitInit(&Wait, fFlags, uTimeout, &pThis->Head);
+ if (RT_SUCCESS(rc))
+ {
+ IPRT_DEBUG_SEMS_STATE(pThis, 'E');
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ rtR0SemLnxWaitPrepare(&Wait);
+
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (ASMAtomicUoReadU32(&pThis->fStateAndGen) != fOrgStateAndGen)
+ rc = VINF_SUCCESS;
+ else if (rtR0SemLnxWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemLnxWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemLnxWaitDoIt(&Wait);
+ continue;
+ }
+ }
+ break;
+ }
+
+ rtR0SemLnxWaitDelete(&Wait);
+ IPRT_DEBUG_SEMS_STATE_RC(pThis, 'E', rc);
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+
+ rtR0SemEventMultiLnxRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventMultiLnxWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventMultiLnxWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitEx);
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventMultiLnxWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventMultiGetResolution(void)
+{
+ return rtR0SemLnxWaitGetResolution();
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiGetResolution);
+
+
+RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
+{
+ return true;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiIsSignalSafe);
+
diff --git a/src/VBox/Runtime/r0drv/linux/semfastmutex-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/semfastmutex-r0drv-linux.c
new file mode 100644
index 00000000..0e2dff35
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/semfastmutex-r0drv-linux.c
@@ -0,0 +1,167 @@
+/* $Id: semfastmutex-r0drv-linux.c $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+#if defined(RT_STRICT) || defined(IPRT_DEBUG_SEMS)
+# include <iprt/thread.h>
+#endif
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the linux semaphore structure.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** the linux semaphore. */
+ struct semaphore Semaphore;
+#if defined(RT_STRICT) || defined(IPRT_DEBUG_SEMS)
+ /** For check. */
+ RTNATIVETHREAD volatile Owner;
+#endif
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Allocate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis;
+ pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize.
+ */
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ sema_init(&pThis->Semaphore, 1);
+#if defined(RT_STRICT) || defined(IPRT_DEBUG_SEMS)
+ pThis->Owner = NIL_RTNATIVETHREAD;
+#endif
+
+ *phFastMtx = pThis;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexCreate);
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ /*
+ * Validate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexDestroy);
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+ IPRT_DEBUG_SEMS_STATE(pThis, 'd');
+ down(&pThis->Semaphore);
+#if defined(RT_STRICT) || defined(IPRT_DEBUG_SEMS)
+ IPRT_DEBUG_SEMS_STATE(pThis, 'o');
+ AssertRelease(pThis->Owner == NIL_RTNATIVETHREAD);
+ ASMAtomicUoWriteSize(&pThis->Owner, RTThreadNativeSelf());
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexRequest);
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+#if defined(RT_STRICT) || defined(IPRT_DEBUG_SEMS)
+ AssertRelease(pThis->Owner == RTThreadNativeSelf());
+ ASMAtomicUoWriteSize(&pThis->Owner, NIL_RTNATIVETHREAD);
+#endif
+ up(&pThis->Semaphore);
+ IPRT_DEBUG_SEMS_STATE(pThis, 'u');
+
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemFastMutexRelease);
+
diff --git a/src/VBox/Runtime/r0drv/linux/semmutex-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/semmutex-r0drv-linux.c
new file mode 100644
index 00000000..16fcacfe
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/semmutex-r0drv-linux.c
@@ -0,0 +1,431 @@
+/* $Id: semmutex-r0drv-linux.c $ */
+/** @file
+ * IPRT - Mutex Semaphores, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 RTSEMMUTEX_WITHOUT_REMAPPING
+#include "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/list.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTSEMMUTEXLNXWAITER
+{
+ /** The list entry. */
+ RTLISTNODE ListEntry;
+ /** The waiting task. */
+ struct task_struct *pTask;
+ /** Why did we wake up? */
+ enum
+ {
+ /** Wakeup to take the semaphore. */
+ RTSEMMUTEXLNXWAITER_WAKEUP,
+ /** Mutex is being destroyed. */
+ RTSEMMUTEXLNXWAITER_DESTROYED,
+ /** Some other reason. */
+ RTSEMMUTEXLNXWAITER_OTHER
+ } volatile enmReason;
+} RTSEMMUTEXLNXWAITER, *PRTSEMMUTEXLNXWAITER;
+
+/**
+ * Wrapper for the linux semaphore structure.
+ */
+typedef struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The number of recursions. */
+ uint32_t cRecursions;
+ /** The list of waiting threads. */
+ RTLISTANCHOR WaiterList;
+ /** The current owner, NULL if none. */
+ struct task_struct *pOwnerTask;
+ /** The number of references to this piece of memory. This is used to
+ * prevent it from being kicked from underneath us while waiting. */
+ uint32_t volatile cRefs;
+ /** The spinlock protecting the members and falling asleep. */
+ spinlock_t Spinlock;
+} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMtx)
+{
+ int rc = VINF_SUCCESS;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Allocate.
+ */
+ PRTSEMMUTEXINTERNAL pThis;
+ pThis = (PRTSEMMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ /*
+ * Initialize.
+ */
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ pThis->cRecursions = 0;
+ pThis->pOwnerTask = NULL;
+ pThis->cRefs = 1;
+ RTListInit(&pThis->WaiterList);
+ spin_lock_init(&pThis->Spinlock);
+
+ *phMtx = pThis;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemMutexCreate);
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMtx)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMtx;
+ PRTSEMMUTEXLNXWAITER pCur;
+ unsigned long fSavedIrq;
+
+ /*
+ * Validate.
+ */
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+ /*
+ * Kill it, kick waiters and release it.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
+ RTListForEach(&pThis->WaiterList, pCur, RTSEMMUTEXLNXWAITER, ListEntry)
+ {
+ pCur->enmReason = RTSEMMUTEXLNXWAITER_DESTROYED;
+ wake_up_process(pCur->pTask);
+ }
+
+ if (ASMAtomicDecU32(&pThis->cRefs) != 0)
+ spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
+ else
+ {
+ spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
+ RTMemFree(pThis);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSemMutexDestroy);
+
+
+/**
+ * Worker for rtSemMutexLinuxRequest that handles the case where we go to sleep.
+ *
+ * @returns VINF_SUCCESS, VERR_INTERRUPTED, VERR_TIMEOUT or VERR_SEM_DESTROYED.
+ * Returns without owning the spinlock.
+ * @param pThis The mutex instance.
+ * @param cMillies The timeout.
+ * @param fInterruptible The wait type.
+ * @param fSavedIrq The saved IRQ flags.
+ */
+static int rtSemMutexLinuxRequestSleep(PRTSEMMUTEXINTERNAL pThis, RTMSINTERVAL cMillies,
+ bool fInterruptible, unsigned long fSavedIrq)
+{
+ struct task_struct *pSelf = current;
+ int rc = VERR_TIMEOUT;
+ long lTimeout = cMillies == RT_INDEFINITE_WAIT ? MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(cMillies);
+ RTSEMMUTEXLNXWAITER Waiter;
+
+ IPRT_DEBUG_SEMS_STATE(pThis, 'm');
+
+ /*
+ * Grab a reference to the mutex and add ourselves to the waiter list.
+ */
+ ASMAtomicIncU32(&pThis->cRefs);
+
+ Waiter.pTask = pSelf;
+ Waiter.enmReason = RTSEMMUTEXLNXWAITER_OTHER;
+ RTListAppend(&pThis->WaiterList, &Waiter.ListEntry);
+
+ /*
+ * Do the waiting.
+ */
+ for (;;)
+ {
+ /* Check signal and timeout conditions. */
+ if ( fInterruptible
+ && signal_pending(pSelf))
+ {
+ rc = VERR_INTERRUPTED;
+ break;
+ }
+
+ if (!lTimeout)
+ break;
+
+ /* Go to sleep. */
+ set_current_state(fInterruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ spin_unlock_irq(&pThis->Spinlock);
+
+ lTimeout = schedule_timeout(lTimeout);
+
+ spin_lock_irq(&pThis->Spinlock);
+ set_current_state(TASK_RUNNING);
+
+ /* Did someone wake us up? */
+ if (Waiter.enmReason == RTSEMMUTEXLNXWAITER_WAKEUP)
+ {
+ Assert(pThis->cRecursions == 0);
+ pThis->cRecursions = 1;
+ pThis->pOwnerTask = pSelf;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Is the mutex being destroyed? */
+ if (RT_UNLIKELY( Waiter.enmReason == RTSEMMUTEXLNXWAITER_DESTROYED
+ || pThis->u32Magic != RTSEMMUTEX_MAGIC))
+ {
+ rc = VERR_SEM_DESTROYED;
+ break;
+ }
+ }
+
+ /*
+ * Unlink ourself from the waiter list, dereference the mutex and exit the
+ * lock. We might have to free the mutex if it was the destroyed.
+ */
+ RTListNodeRemove(&Waiter.ListEntry);
+ IPRT_DEBUG_SEMS_STATE_RC(pThis, 'M', rc);
+
+ if (RT_LIKELY(ASMAtomicDecU32(&pThis->cRefs) != 0))
+ spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
+ else
+ {
+ Assert(RT_FAILURE_NP(rc));
+ spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
+ RTMemFree(pThis);
+ }
+ return rc;
+}
+
+
+/**
+ * Internal worker.
+ */
+DECLINLINE(int) rtSemMutexLinuxRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, bool fInterruptible)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ struct task_struct *pSelf = current;
+ unsigned long fSavedIrq;
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs >= 1);
+
+ /*
+ * Lock it and check if it's a recursion.
+ */
+ spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
+ if (pThis->pOwnerTask == pSelf)
+ {
+ pThis->cRecursions++;
+ Assert(pThis->cRecursions > 1);
+ Assert(pThis->cRecursions < 256);
+ rc = VINF_SUCCESS;
+ }
+ /*
+ * Not a recursion, maybe it's not owned by anyone then?
+ */
+ else if ( pThis->pOwnerTask == NULL
+ && RTListIsEmpty(&pThis->WaiterList))
+ {
+ Assert(pThis->cRecursions == 0);
+ pThis->cRecursions = 1;
+ pThis->pOwnerTask = pSelf;
+ rc = VINF_SUCCESS;
+ }
+ /*
+ * Was it a polling call?
+ */
+ else if (cMillies == 0)
+ rc = VERR_TIMEOUT;
+ /*
+ * No, so go to sleep.
+ */
+ else
+ {
+ rc = rtSemMutexLinuxRequestSleep(pThis, cMillies, fInterruptible, fSavedIrq);
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC();
+ return rc;
+ }
+
+ IPRT_DEBUG_SEMS_STATE_RC(pThis, 'M', rc);
+ spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC();
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexLinuxRequest(hMutexSem, cMillies, false /*fInterruptible*/);
+}
+RT_EXPORT_SYMBOL(RTSemMutexRequest);
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RT_NOREF_PV(uId); RT_SRC_POS_NOREF();
+ return RTSemMutexRequest(hMutexSem, cMillies);
+}
+RT_EXPORT_SYMBOL(RTSemMutexRequestDebug);
+
+
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexLinuxRequest(hMutexSem, cMillies, true /*fInterruptible*/);
+}
+RT_EXPORT_SYMBOL(RTSemMutexRequestNoResume);
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RT_NOREF_PV(uId); RT_SRC_POS_NOREF();
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+RT_EXPORT_SYMBOL(RTSemMutexRequestNoResumeDebug);
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMtx)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMtx;
+ struct task_struct *pSelf = current;
+ unsigned long fSavedIrq;
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs >= 1);
+
+ /*
+ * Take the lock and release one recursion.
+ */
+ spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
+ if (pThis->pOwnerTask == pSelf)
+ {
+ Assert(pThis->cRecursions > 0);
+ if (--pThis->cRecursions == 0)
+ {
+ pThis->pOwnerTask = NULL;
+
+ /* anyone to wake up? */
+ if (!RTListIsEmpty(&pThis->WaiterList))
+ {
+ PRTSEMMUTEXLNXWAITER pWaiter = RTListGetFirst(&pThis->WaiterList, RTSEMMUTEXLNXWAITER, ListEntry);
+ pWaiter->enmReason = RTSEMMUTEXLNXWAITER_WAKEUP;
+ wake_up_process(pWaiter->pTask);
+ }
+ IPRT_DEBUG_SEMS_STATE(pThis, 'u');
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NOT_OWNER;
+ spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
+
+ AssertRC(rc);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSemMutexRelease);
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ unsigned long fSavedIrq;
+ bool fOwned;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pThis, false);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), false);
+ Assert(pThis->cRefs >= 1);
+
+ /*
+ * Take the lock and release one recursion.
+ */
+ spin_lock_irqsave(&pThis->Spinlock, fSavedIrq);
+ fOwned = pThis->pOwnerTask != NULL;
+ spin_unlock_irqrestore(&pThis->Spinlock, fSavedIrq);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return fOwned;
+
+}
+RT_EXPORT_SYMBOL(RTSemMutexIsOwned);
+
diff --git a/src/VBox/Runtime/r0drv/linux/spinlock-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/spinlock-r0drv-linux.c
new file mode 100644
index 00000000..c5bdded2
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/spinlock-r0drv-linux.c
@@ -0,0 +1,196 @@
+/* $Id: spinlock-r0drv-linux.c $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/spinlock.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the spinlock_t structure.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The spinlock creation flags. */
+ uint32_t fFlags;
+ /** The saved interrupt flag. */
+ unsigned long volatile fIntSaved;
+ /** The linux spinlock structure. */
+ spinlock_t Spinlock;
+#ifdef RT_MORE_STRICT
+ /** The idAssertCpu variable before acquring the lock for asserting after
+ * releasing the spinlock. */
+ RTCPUID volatile idAssertCpu;
+ /** The CPU that owns the lock. */
+ RTCPUID volatile idCpuOwner;
+#endif
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ PRTSPINLOCKINTERNAL pThis;
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(pszName);
+
+ /*
+ * Allocate.
+ */
+ Assert(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ pThis = (PRTSPINLOCKINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ /*
+ * Initialize and return.
+ */
+ pThis->u32Magic = RTSPINLOCK_MAGIC;
+ pThis->fFlags = fFlags;
+ pThis->fIntSaved = 0;
+#ifdef RT_MORE_STRICT
+ pThis->idCpuOwner = NIL_RTCPUID;
+ pThis->idAssertCpu = NIL_RTCPUID;
+#endif
+
+ spin_lock_init(&pThis->Spinlock);
+
+ *pSpinlock = pThis;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSpinlockCreate);
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ if (pThis->u32Magic != RTSPINLOCK_MAGIC)
+ {
+ AssertMsgFailed(("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ ASMAtomicIncU32(&pThis->u32Magic);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTSpinlockDestroy);
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ IPRT_LINUX_SAVE_EFL_AC();
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+ AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_MAGIC,
+ ("pThis=%p u32Magic=%08x\n", pThis, pThis ? (int)pThis->u32Magic : 0));
+
+#ifdef CONFIG_PROVE_LOCKING
+ lockdep_off();
+#endif
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+ unsigned long fIntSaved;
+ spin_lock_irqsave(&pThis->Spinlock, fIntSaved);
+ pThis->fIntSaved = fIntSaved;
+ }
+ else
+ spin_lock(&pThis->Spinlock);
+#ifdef CONFIG_PROVE_LOCKING
+ lockdep_on();
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC();
+ RT_ASSERT_PREEMPT_CPUID_SPIN_ACQUIRED(pThis);
+}
+RT_EXPORT_SYMBOL(RTSpinlockAcquire);
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ IPRT_LINUX_SAVE_EFL_AC(); /* spin_unlock* may preempt and trash eflags.ac. */
+ RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE_VARS();
+ AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_MAGIC,
+ ("pThis=%p u32Magic=%08x\n", pThis, pThis ? (int)pThis->u32Magic : 0));
+ RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE(pThis);
+
+#ifdef CONFIG_PROVE_LOCKING
+ lockdep_off();
+#endif
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+ unsigned long fIntSaved = pThis->fIntSaved;
+ pThis->fIntSaved = 0;
+ spin_unlock_irqrestore(&pThis->Spinlock, fIntSaved);
+ }
+ else
+ spin_unlock(&pThis->Spinlock);
+#ifdef CONFIG_PROVE_LOCKING
+ lockdep_on();
+#endif
+
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC();
+ RT_ASSERT_PREEMPT_CPUID();
+}
+RT_EXPORT_SYMBOL(RTSpinlockRelease);
+
diff --git a/src/VBox/Runtime/r0drv/linux/string.h b/src/VBox/Runtime/r0drv/linux/string.h
new file mode 100644
index 00000000..9e5a6941
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/string.h
@@ -0,0 +1,70 @@
+/* $Id: string.h $ */
+/** @file
+ * IPRT - wrapper for the linux kernel asm/string.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 IPRT_INCLUDED_SRC_r0drv_linux_string_h
+#define IPRT_INCLUDED_SRC_r0drv_linux_string_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+
+RT_C_DECLS_BEGIN
+#ifndef bool /* Linux 2.6.19 C++ nightmare */
+#define bool bool_type
+#define true true_type
+#define false false_type
+#define _Bool int
+#define bool_type_r0drv_string_h__
+#endif
+#include <linux/types.h>
+#include <linux/string.h>
+#ifdef bool_type_r0drv_string_h__
+#undef bool
+#undef true
+#undef false
+#undef bool_type_r0drv_string_h__
+#endif
+char *strpbrk(const char *pszStr, const char *pszChars)
+#if defined(__THROW)
+ __THROW
+#endif
+ ;
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_linux_string_h */
+
diff --git a/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h b/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h
new file mode 100644
index 00000000..8e70f992
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h
@@ -0,0 +1,494 @@
+/* $Id: the-linux-kernel.h $ */
+/** @file
+ * IPRT - Include all necessary headers for the Linux kernel.
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_linux_the_linux_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_linux_the_linux_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/*
+ * Include iprt/types.h to install the bool wrappers.
+ * Then use the linux bool type for all the stuff include here.
+ */
+#include <iprt/types.h>
+#define bool linux_bool
+
+#if RT_GNUC_PREREQ(4, 6)
+# pragma GCC diagnostic push
+#endif
+#if RT_GNUC_PREREQ(4, 2)
+# pragma GCC diagnostic ignored "-Wunused-parameter"
+# if !defined(__cplusplus) && RT_GNUC_PREREQ(4, 3)
+# pragma GCC diagnostic ignored "-Wold-style-declaration" /* 2.6.18-411.0.0.0.1.el5/build/include/asm/apic.h:110: warning: 'inline' is not at beginning of declaration [-Wold-style-declaration] */
+# endif
+#endif
+
+
+#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
+
+/* We only support 2.4 and 2.6 series kernels */
+#if RTLNX_VER_MAX(2,4,0)
+# error Sorry, we do not support 2.3 and earlier kernels.
+#endif
+#if RTLNX_VER_MIN(2,5,0) && RTLNX_VER_MAX(2,6,0)
+# error Sorry, we do not support 2.5 series kernels (might work though).
+#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
+# if RTLNX_VER_MIN(3,3,0)
+# include <linux/kconfig.h> /* for macro IS_ENABLED */
+# endif
+#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/module.h>
+#if RTLNX_VER_MIN(2,6,0)
+# include <linux/moduleparam.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#if RTLNX_VER_MIN(2,6,0)
+# include <linux/namei.h>
+#endif
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+
+#if RTLNX_VER_RANGE(3,9,23, 3,9,31)
+# include <linux/splice.h>
+#endif
+
+#if RTLNX_VER_MIN(3,9,0)
+# include <linux/sched/rt.h>
+#endif
+#if RTLNX_VER_MIN(4,11,0)
+# include <linux/sched/signal.h>
+# include <linux/sched/types.h>
+#endif
+#if RTLNX_VER_MIN(2,6,7)
+# include <linux/jiffies.h>
+#endif
+#if RTLNX_VER_MIN(2,6,16)
+# include <linux/ktime.h>
+# include <linux/hrtimer.h>
+#endif
+#include <linux/wait.h>
+#if RTLNX_VER_MIN(2,5,71)
+# include <linux/cpu.h>
+# include <linux/notifier.h>
+#endif
+#if RTLNX_VER_MIN(5,1,0)
+# include <uapi/linux/mman.h>
+#endif
+/* For the basic additions module */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/compiler.h>
+#if RTLNX_VER_MIN(5,9,0) || RTLNX_SUSE_MAJ_PREREQ(15,3) /* linux/fs.h defined HAVE_UNLOCKED_IOCTL from 2.6.11 up to 5.9 (also 5.3.18-56 in SLES15-SP3), when it became an implicit assumption. */
+# define HAVE_UNLOCKED_IOCTL 1 /* We use this in a couple of places, so for now just define it for 5.9+ too. */
+#endif
+#if !defined(HAVE_UNLOCKED_IOCTL) && RTLNX_VER_MAX(2,6,38)
+# include <linux/smp_lock.h>
+#endif
+/* For the shared folders module */
+#include <linux/vmalloc.h>
+#define wchar_t linux_wchar_t
+#include <linux/nls.h>
+#undef wchar_t
+#include <asm/mman.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/div64.h>
+
+/* For thread-context hooks. */
+#if RTLNX_VER_MIN(2,6,18) && defined(CONFIG_PREEMPT_NOTIFIERS)
+# include <linux/preempt.h>
+#endif
+
+/* for workqueue / task queues. */
+#if RTLNX_VER_MIN(2,5,41)
+# include <linux/workqueue.h>
+#else
+# include <linux/tqueue.h>
+#endif
+
+#if RTLNX_VER_MIN(2,6,4)
+# include <linux/kthread.h>
+#endif
+
+/* for cr4_init_shadow() / cpu_tlbstate. */
+#if RTLNX_VER_MIN(3,20,0)
+# include <asm/tlbflush.h>
+#endif
+
+/* for set_pages_x() */
+#if RTLNX_VER_MIN(4,12,0)
+# include <asm/set_memory.h>
+#endif
+
+/* for __flush_tlb_all() */
+#if RTLNX_VER_MIN(2,6,28) && (defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86))
+# include <asm/tlbflush.h>
+#endif
+
+/* for kernel_fpu_begin / kernel_fpu_end() */
+#if RTLNX_VER_MIN(4,2,0)
+# include <asm/fpu/api.h>
+#endif
+
+#if RTLNX_VER_MIN(3,7,0)
+# include <asm/smap.h>
+#else
+static inline void clac(void) { }
+static inline void stac(void) { }
+#endif
+
+#if RTLNX_VER_MAX(2,6,0)
+# ifndef page_to_pfn
+# define page_to_pfn(page) ((page) - mem_map)
+# endif
+#endif
+
+#ifndef DEFINE_WAIT
+# define DEFINE_WAIT(name) DECLARE_WAITQUEUE(name, current)
+#endif
+
+#ifndef __GFP_NOWARN
+# define __GFP_NOWARN 0
+#endif
+
+/*
+ * 2.4 / early 2.6 compatibility wrappers
+ */
+#if RTLNX_VER_MAX(2,6,7)
+
+# ifndef MAX_JIFFY_OFFSET
+# define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)
+# endif
+
+# if RTLNX_VER_MAX(2,4,29) || RTLNX_VER_MIN(2,6,0)
+
+DECLINLINE(unsigned int) jiffies_to_msecs(unsigned long cJiffies)
+{
+# if HZ <= 1000 && !(1000 % HZ)
+ return (1000 / HZ) * cJiffies;
+# elif HZ > 1000 && !(HZ % 1000)
+ return (cJiffies + (HZ / 1000) - 1) / (HZ / 1000);
+# else
+ return (cJiffies * 1000) / HZ;
+# endif
+}
+
+DECLINLINE(unsigned long) msecs_to_jiffies(unsigned int cMillies)
+{
+# if HZ > 1000
+ if (cMillies > jiffies_to_msecs(MAX_JIFFY_OFFSET))
+ return MAX_JIFFY_OFFSET;
+# endif
+# if HZ <= 1000 && !(1000 % HZ)
+ return (cMillies + (1000 / HZ) - 1) / (1000 / HZ);
+# elif HZ > 1000 && !(HZ % 1000)
+ return cMillies * (HZ / 1000);
+# else
+ return (cMillies * HZ + 999) / 1000;
+# endif
+}
+
+# endif /* < 2.4.29 || >= 2.6.0 */
+
+#endif /* < 2.6.7 */
+
+/*
+ * 2.4 compatibility wrappers
+ */
+#if RTLNX_VER_MAX(2,6,0)
+
+# define prepare_to_wait(q, wait, state) \
+ do { \
+ add_wait_queue(q, wait); \
+ set_current_state(state); \
+ } while (0)
+
+# define after_wait(wait) \
+ do { \
+ list_del_init(&(wait)->task_list); \
+ } while (0)
+
+# define finish_wait(q, wait) \
+ do { \
+ set_current_state(TASK_RUNNING); \
+ remove_wait_queue(q, wait); \
+ } while (0)
+
+#else /* >= 2.6.0 */
+
+# define after_wait(wait) do {} while (0)
+
+#endif /* >= 2.6.0 */
+
+/** @def TICK_NSEC
+ * The time between ticks in nsec */
+#ifndef TICK_NSEC
+# define TICK_NSEC (1000000000UL / HZ)
+#endif
+
+/*
+ * This sucks soooo badly on x86! Why don't they export __PAGE_KERNEL_EXEC so PAGE_KERNEL_EXEC would be usable?
+ */
+#if RTLNX_VER_MIN(2,6,8) && defined(RT_ARCH_AMD64)
+# define MY_PAGE_KERNEL_EXEC PAGE_KERNEL_EXEC
+#elif RTLNX_VER_MIN(2,6,8) && defined(PAGE_KERNEL_EXEC) && defined(CONFIG_X86_PAE)
+# ifdef __PAGE_KERNEL_EXEC
+ /* >= 2.6.27 */
+# define MY_PAGE_KERNEL_EXEC __pgprot(boot_cpu_has(X86_FEATURE_PGE) ? __PAGE_KERNEL_EXEC | _PAGE_GLOBAL : __PAGE_KERNEL_EXEC)
+# else
+# define MY_PAGE_KERNEL_EXEC __pgprot(boot_cpu_has(X86_FEATURE_PGE) ? _PAGE_KERNEL_EXEC | _PAGE_GLOBAL : _PAGE_KERNEL_EXEC)
+# endif
+#else
+# define MY_PAGE_KERNEL_EXEC PAGE_KERNEL
+#endif
+
+
+/*
+ * The redhat hack section.
+ * - The current hacks are for 2.4.21-15.EL only.
+ */
+#ifndef NO_REDHAT_HACKS
+/* accounting. */
+# if RTLNX_VER_MAX(2,6,0)
+# ifdef VM_ACCOUNT
+# define USE_RHEL4_MUNMAP
+# endif
+# endif
+
+/* backported remap_page_range. */
+# if RTLNX_VER_MAX(2,6,0)
+# include <asm/tlb.h>
+# ifdef tlb_vma /* probably not good enough... */
+# define HAVE_26_STYLE_REMAP_PAGE_RANGE 1
+# endif
+# endif
+
+# ifndef RT_ARCH_AMD64
+/* In 2.6.9-22.ELsmp we have to call change_page_attr() twice when changing
+ * the page attributes from PAGE_KERNEL to something else, because there appears
+ * to be a bug in one of the many patches that redhat applied.
+ * It should be safe to do this on less buggy linux kernels too. ;-)
+ */
+# define MY_CHANGE_PAGE_ATTR(pPages, cPages, prot) \
+ do { \
+ if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) \
+ change_page_attr(pPages, cPages, prot); \
+ change_page_attr(pPages, cPages, prot); \
+ } while (0)
+# endif /* !RT_ARCH_AMD64 */
+#endif /* !NO_REDHAT_HACKS */
+
+#ifndef MY_CHANGE_PAGE_ATTR
+# ifdef RT_ARCH_AMD64 /** @todo This is a cheap hack, but it'll get around that 'else BUG();' in __change_page_attr(). */
+# define MY_CHANGE_PAGE_ATTR(pPages, cPages, prot) \
+ do { \
+ change_page_attr(pPages, cPages, PAGE_KERNEL_NOCACHE); \
+ change_page_attr(pPages, cPages, prot); \
+ } while (0)
+# else
+# define MY_CHANGE_PAGE_ATTR(pPages, cPages, prot) change_page_attr(pPages, cPages, prot)
+# endif
+#endif
+
+#if RTLNX_VER_MIN(2,6,25)
+# if RTLNX_VER_MAX(5,4,0) /* The interface was removed, but we only need it for < 2.4.22, so who cares. */
+# define MY_SET_PAGES_EXEC(pPages, cPages) set_pages_x(pPages, cPages)
+# define MY_SET_PAGES_NOEXEC(pPages, cPages) set_pages_nx(pPages, cPages)
+# endif
+#else
+# define MY_SET_PAGES_EXEC(pPages, cPages) \
+ do { \
+ if (pgprot_val(MY_PAGE_KERNEL_EXEC) != pgprot_val(PAGE_KERNEL)) \
+ MY_CHANGE_PAGE_ATTR(pPages, cPages, MY_PAGE_KERNEL_EXEC); \
+ } while (0)
+# define MY_SET_PAGES_NOEXEC(pPages, cPages) \
+ do { \
+ if (pgprot_val(MY_PAGE_KERNEL_EXEC) != pgprot_val(PAGE_KERNEL)) \
+ MY_CHANGE_PAGE_ATTR(pPages, cPages, PAGE_KERNEL); \
+ } while (0)
+#endif
+
+/** @def ONE_MSEC_IN_JIFFIES
+ * The number of jiffies that make up 1 millisecond. Must be at least 1! */
+#if HZ <= 1000
+# define ONE_MSEC_IN_JIFFIES 1
+#elif !(HZ % 1000)
+# define ONE_MSEC_IN_JIFFIES (HZ / 1000)
+#else
+# define ONE_MSEC_IN_JIFFIES ((HZ + 999) / 1000)
+# error "HZ is not a multiple of 1000, the GIP stuff won't work right!"
+#endif
+
+/*
+ * Stop using the linux bool type.
+ */
+#undef bool
+
+#if RT_GNUC_PREREQ(4, 6)
+# pragma GCC diagnostic pop
+#endif
+
+/*
+ * There are post-2.6.24 kernels (confusingly with unchanged version number)
+ * which eliminate macros which were marked as deprecated.
+ */
+#ifndef __attribute_used__
+#define __attribute_used__ __used
+#endif
+
+/**
+ * Hack for shortening pointers on linux so we can stuff more stuff into the
+ * task_struct::comm field. This is used by the semaphore code but put here
+ * because we don't have any better place atm. Don't use outside IPRT, please.
+ */
+#ifdef RT_ARCH_AMD64
+# define IPRT_DEBUG_SEMS_ADDRESS(addr) ( ((long)(addr) & (long)~UINT64_C(0xfffffff000000000)) )
+#else
+# define IPRT_DEBUG_SEMS_ADDRESS(addr) ( (long)(addr) )
+#endif
+
+/**
+ * Puts semaphore info into the task_struct::comm field if IPRT_DEBUG_SEMS is
+ * defined.
+ */
+#ifdef IPRT_DEBUG_SEMS
+# define IPRT_DEBUG_SEMS_STATE(pThis, chState) \
+ snprintf(current->comm, sizeof(current->comm), "%c%lx", (chState), IPRT_DEBUG_SEMS_ADDRESS(pThis));
+#else
+# define IPRT_DEBUG_SEMS_STATE(pThis, chState) do { } while (0)
+#endif
+
+/**
+ * Puts semaphore info into the task_struct::comm field if IPRT_DEBUG_SEMS is
+ * defined.
+ */
+#ifdef IPRT_DEBUG_SEMS
+# define IPRT_DEBUG_SEMS_STATE_RC(pThis, chState, rc) \
+ snprintf(current->comm, sizeof(current->comm), "%c%lx:%d", (chState), IPRT_DEBUG_SEMS_ADDRESS(pThis), rc);
+#else
+# define IPRT_DEBUG_SEMS_STATE_RC(pThis, chState, rc) do { } while (0)
+#endif
+
+/** @name Macros for preserving EFLAGS.AC on 3.19+/amd64 paranoid.
+ * The AMD 64 switch_to in macro in arch/x86/include/asm/switch_to.h stopped
+ * restoring flags.
+ * @{ */
+#if (defined(CONFIG_X86_SMAP) || defined(RT_STRICT) || defined(IPRT_WITH_EFLAGS_AC_PRESERVING)) \
+ && !defined(IPRT_WITHOUT_EFLAGS_AC_PRESERVING)
+# include <iprt/asm-amd64-x86.h>
+# define IPRT_X86_EFL_AC RT_BIT(18)
+# define IPRT_LINUX_SAVE_EFL_AC() RTCCUINTREG fSavedEfl = ASMGetFlags()
+# define IPRT_LINUX_RESTORE_EFL_AC() ASMSetFlags(fSavedEfl)
+# define IPRT_LINUX_RESTORE_EFL_ONLY_AC() ASMChangeFlags(~IPRT_X86_EFL_AC, fSavedEfl & IPRT_X86_EFL_AC)
+#else
+# define IPRT_LINUX_SAVE_EFL_AC() do { } while (0)
+# define IPRT_LINUX_RESTORE_EFL_AC() do { } while (0)
+# define IPRT_LINUX_RESTORE_EFL_ONLY_AC() do { } while (0)
+#endif
+/** @} */
+
+/*
+ * There are some conflicting defines in iprt/param.h, sort them out here.
+ */
+#ifndef IPRT_INCLUDED_param_h
+# undef PAGE_SIZE
+# undef PAGE_OFFSET_MASK
+# include <iprt/param.h>
+#endif
+
+/*
+ * Some global indicator macros.
+ */
+/** @def IPRT_LINUX_HAS_HRTIMER
+ * Whether the kernel support high resolution timers (Linux kernel versions
+ * 2.6.28 and later (hrtimer_add_expires_ns() & schedule_hrtimeout). */
+#if RTLNX_VER_MIN(2,6,28) || defined(DOXYGEN_RUNNING)
+# define IPRT_LINUX_HAS_HRTIMER
+#endif
+
+/*
+ * Workqueue stuff, see initterm-r0drv-linux.c.
+ */
+#if RTLNX_VER_MIN(2,5,41)
+typedef struct work_struct RTR0LNXWORKQUEUEITEM;
+#else
+typedef struct tq_struct RTR0LNXWORKQUEUEITEM;
+#endif
+DECLHIDDEN(void) rtR0LnxWorkqueuePush(RTR0LNXWORKQUEUEITEM *pWork, void (*pfnWorker)(RTR0LNXWORKQUEUEITEM *));
+DECLHIDDEN(void) rtR0LnxWorkqueueFlush(void);
+
+/*
+ * Memory hacks from memobj-r0drv-linux.c that shared folders need.
+ */
+RTDECL(struct page *) rtR0MemObjLinuxVirtToPage(void *pv);
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_linux_the_linux_kernel_h */
diff --git a/src/VBox/Runtime/r0drv/linux/thread-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/thread-r0drv-linux.c
new file mode 100644
index 00000000..2724d091
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/thread-r0drv-linux.c
@@ -0,0 +1,285 @@
+/* $Id: thread-r0drv-linux.c $ */
+/** @file
+ * IPRT - Threads, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#include <iprt/asm.h>
+#if RTLNX_VER_MAX(2,5,28) || defined(CONFIG_X86_SMAP)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mp.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if defined(CONFIG_PREEMPT_COUNT) /* since 3.1 */ \
+ || defined(CONFIG_PREEMPTION) /* since 5.3 */ \
+ || defined(CONFIG_PREEMPT) /* since 2.6.13 - preemption model choice; before that arch specific choice back to 2.5.45. */
+# define IPRT_LNX_HAVE_PREEMPTION
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifndef IPRT_LNX_HAVE_PREEMPTION
+/** Per-cpu preemption counters. */
+static int32_t volatile g_acPreemptDisabled[NR_CPUS];
+#endif
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)current;
+}
+RT_EXPORT_SYMBOL(RTThreadNativeSelf);
+
+
+static int rtR0ThreadLnxSleepCommon(RTMSINTERVAL cMillies)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+ long cJiffies = msecs_to_jiffies(cMillies);
+ set_current_state(TASK_INTERRUPTIBLE);
+ cJiffies = schedule_timeout(cJiffies);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ if (!cJiffies)
+ return VINF_SUCCESS;
+ return VERR_INTERRUPTED;
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadLnxSleepCommon(cMillies);
+}
+RT_EXPORT_SYMBOL(RTThreadSleep);
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadLnxSleepCommon(cMillies);
+}
+RT_EXPORT_SYMBOL(RTThreadSleepNoLog);
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+#if RTLNX_VER_MIN(2,4,20)
+ yield();
+#else
+ /** @todo r=ramshankar: Can we use cond_resched() instead? */
+ set_current_state(TASK_RUNNING);
+ sys_sched_yield();
+ schedule();
+#endif
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return true;
+}
+RT_EXPORT_SYMBOL(RTThreadYield);
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+#ifdef IPRT_LNX_HAVE_PREEMPTION
+ Assert(hThread == NIL_RTTHREAD); RT_NOREF_PV(hThread);
+# ifdef preemptible
+ return preemptible();
+# else
+ return preempt_count() == 0 && !in_atomic() && !irqs_disabled();
+# endif
+#else
+ int32_t c;
+
+ Assert(hThread == NIL_RTTHREAD);
+ c = g_acPreemptDisabled[smp_processor_id()];
+ AssertMsg(c >= 0 && c < 32, ("%d\n", c));
+ if (c != 0)
+ return false;
+# if RTLNX_VER_MIN(2,5,32)
+ if (in_atomic())
+ return false;
+# endif
+# if RTLNX_VER_MIN(2,5,28)
+ if (irqs_disabled())
+ return false;
+# else
+ if (!ASMIntAreEnabled())
+ return false;
+# endif
+ return true;
+#endif
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptIsEnabled);
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); RT_NOREF_PV(hThread);
+#if RTLNX_VER_MIN(2,5,4)
+ return !!test_tsk_thread_flag(current, TIF_NEED_RESCHED);
+
+#elif RTLNX_VER_MIN(2,4,20)
+ return !!need_resched();
+
+#elif RTLNX_VER_MIN(2,1,110)
+ return current->need_resched != 0;
+
+#else
+ return need_resched != 0;
+#endif
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptIsPending);
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* yes, RTThreadPreemptIsPending is reliable. */
+ return true;
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptIsPendingTrusty);
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+#ifdef IPRT_LNX_HAVE_PREEMPTION
+ return true; /* Yes, kernel preemption is possible. */
+#else
+ return false; /* No kernel preemption (or just CONFIG_PREEMPT_VOLUNTARY). */
+#endif
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptIsPossible);
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+#ifdef IPRT_LNX_HAVE_PREEMPTION
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 0);
+ pState->u32Reserved = 42;
+ preempt_disable();
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+
+#else /* !IPRT_LNX_HAVE_PREEMPTION */
+ int32_t c;
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 0);
+
+ /* Do our own accounting. */
+ c = ASMAtomicIncS32(&g_acPreemptDisabled[smp_processor_id()]);
+ AssertMsg(c > 0 && c < 32, ("%d\n", c));
+ pState->u32Reserved = c;
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+#endif
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptDisable);
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+#ifdef IPRT_LNX_HAVE_PREEMPTION
+ IPRT_LINUX_SAVE_EFL_AC(); /* paranoia */
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 42);
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+ preempt_enable();
+ IPRT_LINUX_RESTORE_EFL_ONLY_AC(); /* paranoia */
+
+#else
+ int32_t volatile *pc;
+ AssertPtr(pState);
+ AssertMsg(pState->u32Reserved > 0 && pState->u32Reserved < 32, ("%d\n", pState->u32Reserved));
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+
+ /* Do our own accounting. */
+ pc = &g_acPreemptDisabled[smp_processor_id()];
+ AssertMsg(pState->u32Reserved == (uint32_t)*pc, ("u32Reserved=%d *pc=%d \n", pState->u32Reserved, *pc));
+ ASMAtomicUoWriteS32(pc, pState->u32Reserved - 1);
+#endif
+ pState->u32Reserved = 0;
+}
+RT_EXPORT_SYMBOL(RTThreadPreemptRestore);
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); NOREF(hThread);
+
+ return in_interrupt() != 0;
+}
+RT_EXPORT_SYMBOL(RTThreadIsInInterrupt);
+
+
+RTDECL(int) RTThreadQueryTerminationStatus(RTTHREAD hThread)
+{
+ struct task_struct *pTask = current;
+ AssertReturn(hThread == NIL_RTTHREAD, VERR_NOT_SUPPORTED);
+
+ /* Check out pending signals. ASSUMES we can get away w/o locking
+ anything because we're only reading the data. */
+ if (sigismember(&pTask->pending.signal, SIGKILL))
+ return VINF_THREAD_IS_TERMINATING;
+
+#if RTLNX_VER_MIN(2,5,34)
+ /* Check the pending signals shared with other threads in
+ the same process/group. ASSUME since we're alive that
+ the signal_struct won't be freed while we're looking
+ at it here... */
+ {
+# if RTLNX_VER_MIN(2,5,60)
+ struct signal_struct *pSignal = current->signal;
+# else
+ struct signal_struct *pSignal = current->sig;
+# endif
+ if ( pSignal
+ && sigismember(&pSignal->shared_pending.signal, SIGKILL))
+ return VINF_THREAD_IS_TERMINATING;
+ }
+#endif
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTThreadQueryTerminationStatus);
+
diff --git a/src/VBox/Runtime/r0drv/linux/thread2-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/thread2-r0drv-linux.c
new file mode 100644
index 00000000..16cebd7f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/thread2-r0drv-linux.c
@@ -0,0 +1,243 @@
+/* $Id: thread2-r0drv-linux.c $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+#include "internal/thread.h"
+
+#if RTLNX_VER_MIN(4,11,0)
+ #include <uapi/linux/sched/types.h>
+#endif /* >= KERNEL_VERSION(4, 11, 0) */
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative((RTNATIVETHREAD)current);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+#if RTLNX_VER_MIN(2,5,2)
+ /*
+ * Assignments are partially based on g_aTypesLinuxFree but
+ * scaled up in the high priority end.
+ *
+ * 5.9.0 - :
+ * The sched_set_normal interfaces does not really check the input,
+ * whereas sched_set_fifo & sched_set_fifo_low have fixed assignments.
+ * 2.6.11 - 5.9.0:
+ * Use sched_setscheduler to try effect FIFO scheduling
+ * for IO and TIMER threads, otherwise use set_user_nice.
+ * 2.5.2 - 5.9.0:
+ * Use set_user_nice to renice the thread.
+ */
+ int iNice = 0;
+# if RTLNX_VER_MAX(5,9,0)
+ int rc;
+# if RTLNX_VER_MIN(2,6,11)
+ int iSchedClass = SCHED_NORMAL;
+ struct sched_param Param = { .sched_priority = 0 };
+# endif
+# endif
+ switch (enmType)
+ {
+ case RTTHREADTYPE_INFREQUENT_POLLER:
+ iNice = +3;
+ break;
+
+ case RTTHREADTYPE_MAIN_HEAVY_WORKER:
+ iNice = +2;
+ break;
+
+ case RTTHREADTYPE_EMULATION:
+ iNice = +1;
+ break;
+
+ case RTTHREADTYPE_DEFAULT:
+ case RTTHREADTYPE_GUI:
+ case RTTHREADTYPE_MAIN_WORKER:
+ iNice = 0;
+ break;
+
+ case RTTHREADTYPE_VRDP_IO:
+ case RTTHREADTYPE_DEBUGGER:
+ iNice = -1;
+ break;
+
+ case RTTHREADTYPE_MSG_PUMP:
+ iNice = -2;
+ break;
+
+ case RTTHREADTYPE_IO:
+# if RTLNX_VER_MIN(5,9,0)
+ sched_set_fifo_low(current);
+ return VINF_SUCCESS;
+# else
+ iNice = -12;
+# if RTLNX_VER_MIN(2,6,11)
+ iSchedClass = SCHED_FIFO;
+ Param.sched_priority = 1; /* => prio=98; */
+# endif
+ break;
+# endif
+
+ case RTTHREADTYPE_TIMER:
+# if RTLNX_VER_MIN(5,9,0)
+ sched_set_fifo(current);
+ return VINF_SUCCESS;
+# else
+ iNice = -19;
+# if RTLNX_VER_MIN(2,6,11)
+ iSchedClass = SCHED_FIFO;
+ Param.sched_priority = MAX_RT_PRIO / 2; /* => prio=49 */
+# endif
+ break;
+# endif
+ default:
+ AssertMsgFailedReturn(("enmType=%d\n", enmType), VERR_INVALID_PARAMETER);
+ }
+
+# if RTLNX_VER_MIN(5,9,0)
+ /*
+ * We only get here for renice work.
+ */
+ sched_set_normal(current, iNice);
+
+# else /* < 5.9.0 */
+# if RTLNX_VER_MIN(2,6,11)
+ /*
+ * Try set scheduler parameters.
+ * Fall back on normal + nice if this fails for FIFO policy.*
+ */
+ rc = sched_setscheduler(current, iSchedClass, &Param);
+ if (rc)
+ {
+ Param.sched_priority = 0;
+ iSchedClass = SCHED_NORMAL;
+ rc = sched_setscheduler(current, iSchedClass, &Param);
+ }
+
+ /*
+ * Renice if using normal scheduling class.
+ */
+ if (iSchedClass == SCHED_NORMAL)
+# endif /* >= 2.6.11 */
+ set_user_nice(current, iNice);
+
+# endif /* < 5.9.0 */
+#else /* < 2.5.2 */
+ RT_NOREF_PV(enmType);
+#endif /* < 2.5.2 */
+ RT_NOREF_PV(pThread);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ RT_NOREF_PV(pThread);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ /** @todo fix RTThreadWait/RTR0Term race on linux. */
+ RTThreadSleep(1); NOREF(pThread);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+#if RTLNX_VER_MIN(2,6,4)
+/**
+ * Native kernel thread wrapper function.
+ *
+ * This will forward to rtThreadMain and do termination upon return.
+ *
+ * @param pvArg Pointer to the argument package.
+ */
+static int rtThreadNativeMain(void *pvArg)
+{
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArg;
+
+ rtThreadMain(pThread, (RTNATIVETHREAD)current, &pThread->szName[0]);
+ return 0;
+}
+#endif
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+#if RTLNX_VER_MIN(2,6,4)
+ struct task_struct *NativeThread;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ RT_ASSERT_PREEMPTIBLE();
+
+ NativeThread = kthread_run(rtThreadNativeMain, pThreadInt, "iprt-%s", pThreadInt->szName);
+
+ if (!IS_ERR(NativeThread))
+ {
+ *pNativeThread = (RTNATIVETHREAD)NativeThread;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_GENERAL_FAILURE;
+#else
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r0drv/linux/threadctxhooks-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/threadctxhooks-r0drv-linux.c
new file mode 100644
index 00000000..a7b7d905
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/threadctxhooks-r0drv-linux.c
@@ -0,0 +1,341 @@
+/* $Id: threadctxhooks-r0drv-linux.c $ */
+/** @file
+ * IPRT - Thread Context Switching Hook, Ring-0 Driver, Linux.
+ */
+
+/*
+ * Copyright (C) 2013-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 "internal/iprt.h"
+
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include "internal/thread.h"
+
+
+/*
+ * Linux kernel 2.6.23 introduced preemption notifiers but RedHat 2.6.18 kernels
+ * got it backported.
+ */
+#if RTLNX_VER_MIN(2,6,18) && defined(CONFIG_PREEMPT_NOTIFIERS)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal hook object for linux.
+ */
+typedef struct RTTHREADCTXHOOKINT
+{
+ /** Magic value (RTTHREADCTXHOOKINT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The thread handle (owner) for which the hook is registered. */
+ RTNATIVETHREAD hOwner;
+ /** The preemption notifier object. */
+ struct preempt_notifier LnxPreemptNotifier;
+ /** Whether the hook is enabled or not. If enabled, the LnxPreemptNotifier
+ * is linked into the owning thread's list of preemption callouts. */
+ bool fEnabled;
+ /** Pointer to the user callback. */
+ PFNRTTHREADCTXHOOK pfnCallback;
+ /** User argument passed to the callback. */
+ void *pvUser;
+ /** The linux callbacks. */
+ struct preempt_ops PreemptOps;
+#if RTLNX_VER_MIN(3,1,19) && defined(RT_ARCH_AMD64)
+ /** Starting with 3.1.19, the linux kernel doesn't restore kernel RFLAGS during
+ * task switch, so we have to do that ourselves. (x86 code is not affected.) */
+ RTCCUINTREG fSavedRFlags;
+#endif
+} RTTHREADCTXHOOKINT;
+typedef RTTHREADCTXHOOKINT *PRTTHREADCTXHOOKINT;
+
+
+/**
+ * Hook function for the thread schedule out event.
+ *
+ * @param pPreemptNotifier Pointer to the preempt_notifier struct.
+ * @param pNext Pointer to the task that is being scheduled
+ * instead of the current thread.
+ *
+ * @remarks Called with the rq (runqueue) lock held and with preemption and
+ * interrupts disabled!
+ */
+static void rtThreadCtxHooksLnxSchedOut(struct preempt_notifier *pPreemptNotifier, struct task_struct *pNext)
+{
+ PRTTHREADCTXHOOKINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXHOOKINT, LnxPreemptNotifier);
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ RTCCUINTREG fSavedEFlags = ASMGetFlags();
+ stac();
+#endif
+ RT_NOREF_PV(pNext);
+
+ AssertPtr(pThis);
+ AssertPtr(pThis->pfnCallback);
+ Assert(pThis->fEnabled);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+
+ pThis->pfnCallback(RTTHREADCTXEVENT_OUT, pThis->pvUser);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMSetFlags(fSavedEFlags);
+# if RTLNX_VER_MIN(3,1,19) && defined(RT_ARCH_AMD64)
+ pThis->fSavedRFlags = fSavedEFlags;
+# endif
+#endif
+}
+
+
+/**
+ * Hook function for the thread schedule in event.
+ *
+ * @param pPreemptNotifier Pointer to the preempt_notifier struct.
+ * @param iCpu The CPU this thread is being scheduled on.
+ *
+ * @remarks Called without holding the rq (runqueue) lock and with preemption
+ * enabled!
+ * @todo r=bird: Preemption is of course disabled when it is called.
+ */
+static void rtThreadCtxHooksLnxSchedIn(struct preempt_notifier *pPreemptNotifier, int iCpu)
+{
+ PRTTHREADCTXHOOKINT pThis = RT_FROM_MEMBER(pPreemptNotifier, RTTHREADCTXHOOKINT, LnxPreemptNotifier);
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ RTCCUINTREG fSavedEFlags = ASMGetFlags();
+ stac();
+#endif
+ RT_NOREF_PV(iCpu);
+
+ AssertPtr(pThis);
+ AssertPtr(pThis->pfnCallback);
+ Assert(pThis->fEnabled);
+
+ pThis->pfnCallback(RTTHREADCTXEVENT_IN, pThis->pvUser);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# if RTLNX_VER_MIN(3,1,19) && defined(RT_ARCH_AMD64)
+ fSavedEFlags &= ~RT_BIT_64(18) /*X86_EFL_AC*/;
+ fSavedEFlags |= pThis->fSavedRFlags & RT_BIT_64(18) /*X86_EFL_AC*/;
+# endif
+ ASMSetFlags(fSavedEFlags);
+#endif
+}
+
+
+/**
+ * Worker function for RTThreadCtxHooks(Deregister|Release)().
+ *
+ * @param pThis Pointer to the internal thread-context object.
+ */
+DECLINLINE(void) rtThreadCtxHookDisable(PRTTHREADCTXHOOKINT pThis)
+{
+ Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
+ Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
+ preempt_disable();
+ preempt_notifier_unregister(&pThis->LnxPreemptNotifier);
+ pThis->fEnabled = false;
+ preempt_enable();
+}
+
+
+RTDECL(int) RTThreadCtxHookCreate(PRTTHREADCTXHOOK phCtxHook, uint32_t fFlags, PFNRTTHREADCTXHOOK pfnCallback, void *pvUser)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis;
+ Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
+
+ /*
+ * Allocate and initialize a new hook. We don't register it yet, just
+ * create it.
+ */
+ pThis = (PRTTHREADCTXHOOKINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_UNLIKELY(!pThis))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_NO_MEMORY;
+ }
+ pThis->u32Magic = RTTHREADCTXHOOKINT_MAGIC;
+ pThis->hOwner = RTThreadNativeSelf();
+ pThis->fEnabled = false;
+ pThis->pfnCallback = pfnCallback;
+ pThis->pvUser = pvUser;
+ preempt_notifier_init(&pThis->LnxPreemptNotifier, &pThis->PreemptOps);
+ pThis->PreemptOps.sched_out = rtThreadCtxHooksLnxSchedOut;
+ pThis->PreemptOps.sched_in = rtThreadCtxHooksLnxSchedIn;
+
+#if RTLNX_VER_MIN(4,2,0)
+ preempt_notifier_inc();
+#endif
+
+ *phCtxHook = pThis;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookCreate);
+
+
+RTDECL(int ) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ if (pThis == NIL_RTTHREADCTXHOOK)
+ return VINF_SUCCESS;
+ AssertPtr(pThis);
+ AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
+ VERR_INVALID_HANDLE);
+ Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ Assert(!pThis->fEnabled || pThis->hOwner == RTThreadNativeSelf());
+
+ /*
+ * If there's still a registered thread-context hook, deregister it now before destroying the object.
+ */
+ if (pThis->fEnabled)
+ {
+ Assert(pThis->hOwner == RTThreadNativeSelf());
+ rtThreadCtxHookDisable(pThis);
+ Assert(!pThis->fEnabled); /* paranoia */
+ }
+
+#if RTLNX_VER_MIN(4,2,0)
+ preempt_notifier_dec();
+#endif
+
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
+ RTMemFree(pThis);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookDestroy);
+
+
+RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
+{
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ AssertPtr(pThis);
+ AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
+ VERR_INVALID_HANDLE);
+ Assert(pThis->hOwner == RTThreadNativeSelf());
+ Assert(!pThis->fEnabled);
+ if (!pThis->fEnabled)
+ {
+ IPRT_LINUX_SAVE_EFL_AC();
+ Assert(pThis->PreemptOps.sched_out == rtThreadCtxHooksLnxSchedOut);
+ Assert(pThis->PreemptOps.sched_in == rtThreadCtxHooksLnxSchedIn);
+
+ /*
+ * Register the callback.
+ */
+ preempt_disable();
+ pThis->fEnabled = true;
+ preempt_notifier_register(&pThis->LnxPreemptNotifier);
+ preempt_enable();
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookEnable);
+
+
+RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
+{
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ if (pThis != NIL_RTTHREADCTXHOOK)
+ {
+ AssertPtr(pThis);
+ AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
+ VERR_INVALID_HANDLE);
+ Assert(pThis->hOwner == RTThreadNativeSelf());
+
+ /*
+ * Deregister the callback.
+ */
+ if (pThis->fEnabled)
+ {
+ IPRT_LINUX_SAVE_EFL_AC();
+ rtThreadCtxHookDisable(pThis);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ }
+ }
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookDisable);
+
+
+RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
+{
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ if (pThis == NIL_RTTHREADCTXHOOK)
+ return false;
+ AssertPtr(pThis);
+ AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
+ false);
+
+ return pThis->fEnabled;
+}
+RT_EXPORT_SYMBOL(RTThreadCtxHookIsEnabled);
+
+#else /* Not supported / Not needed */
+# include "../generic/threadctxhooks-r0drv-generic.cpp"
+#endif /* Not supported / Not needed */
+
diff --git a/src/VBox/Runtime/r0drv/linux/time-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/time-r0drv-linux.c
new file mode 100644
index 00000000..370afbf1
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/time-r0drv-linux.c
@@ -0,0 +1,221 @@
+/* $Id: time-r0drv-linux.c $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 RTLOGGROUP_TIME
+#include "the-linux-kernel.h"
+#include "internal/iprt.h"
+/* Make sure we have the setting functions we need for RTTimeNow: */
+#if RTLNX_VER_MAX(2,6,16)
+# define RTTIME_INCL_TIMEVAL
+#elif RTLNX_VER_MAX(3,17,0)
+# define RTTIME_INCL_TIMESPEC
+#endif
+#include <iprt/time.h>
+#include <iprt/asm.h>
+
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+#if RTLNX_VER_MIN(5,6,0)
+ /*
+ * Starting with kernel version 5.6-rc3 only 64-bit time interfaces
+ * are allowed in the kernel.
+ */
+ uint64_t u64;
+ struct timespec64 Ts = { 0, 0 };
+
+ ktime_get_ts64(&Ts);
+ u64 = Ts.tv_sec * RT_NS_1SEC_64 + Ts.tv_nsec;
+ return u64;
+
+#elif RTLNX_VER_MIN(2,6,16) /* This must match timer-r0drv-linux.c! */
+ /*
+ * Use ktime_get_ts, this is also what clock_gettime(CLOCK_MONOTONIC,) is using.
+ */
+ uint64_t u64;
+ struct timespec Ts = { 0, 0 };
+ ktime_get_ts(&Ts);
+ u64 = Ts.tv_sec * RT_NS_1SEC_64 + Ts.tv_nsec;
+ return u64;
+
+#elif RTLNX_VER_MIN(2,5,60)
+ /*
+ * Seems there is no way of getting to the exact source of
+ * sys_clock_gettime(CLOCK_MONOTONIC, &ts) here, I think. But
+ * 64-bit jiffies adjusted for the initial value should be pretty
+ * much the same I hope.
+ */
+ uint64_t u64 = get_jiffies_64();
+# ifdef INITIAL_JIFFIES
+ u64 += INITIAL_JIFFIES;
+# endif
+ u64 *= TICK_NSEC;
+ return u64;
+
+#else /* < 2.5.60 */
+# if BITS_PER_LONG >= 64
+ /*
+ * This is the same as above, except that there is no get_jiffies_64()
+ * here and we rely on long, and therefor jiffies, being 64-bit instead.
+ */
+ uint64_t u64 = jiffies;
+# ifdef INITIAL_JIFFIES
+ u64 += INITIAL_JIFFIES;
+# endif
+ u64 *= TICK_NSEC;
+ return u64;
+
+# else /* 32 bit jiffies */
+ /*
+ * We'll have to try track jiffy rollovers here or we'll be
+ * in trouble every time it flips.
+ *
+ * The high dword of the s_u64Last is the rollover count, the
+ * low dword is the previous jiffies. Updating is done by
+ * atomic compare & exchange of course.
+ */
+ static uint64_t volatile s_u64Last = 0;
+ uint64_t u64;
+
+ for (;;)
+ {
+ uint64_t u64NewLast;
+ int32_t iDelta;
+ uint32_t cRollovers;
+ uint32_t u32LastJiffies;
+
+ /* sample the values */
+ unsigned long ulNow = jiffies;
+ uint64_t u64Last = s_u64Last;
+ if (ulNow != jiffies)
+ continue; /* try again */
+# ifdef INITIAL_JIFFIES
+ ulNow += INITIAL_JIFFIES;
+# endif
+
+ u32LastJiffies = (uint32_t)u64Last;
+ cRollovers = u64Last >> 32;
+
+ /*
+ * Check for rollover and update the static last value.
+ *
+ * We have to make sure we update it successfully to rule out
+ * an underrun because of racing someone.
+ */
+ iDelta = ulNow - u32LastJiffies;
+ if (iDelta < 0)
+ {
+ cRollovers++;
+ u64NewLast = RT_MAKE_U64(ulNow, cRollovers);
+ if (!ASMAtomicCmpXchgU64(&s_u64Last, u64NewLast, u64Last))
+ continue; /* race, try again */
+ }
+ else
+ {
+ u64NewLast = RT_MAKE_U64(ulNow, cRollovers);
+ ASMAtomicCmpXchgU64(&s_u64Last, u64NewLast, u64Last);
+ }
+
+ /* calculate the return value */
+ u64 = ulNow;
+ u64 *= TICK_NSEC;
+ u64 += cRollovers * (_4G * TICK_NSEC);
+ break;
+ }
+
+ return u64;
+# endif /* 32 bit jiffies */
+#endif /* < 2.5.60 */
+}
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+RT_EXPORT_SYMBOL(RTTimeNanoTS);
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+RT_EXPORT_SYMBOL(RTTimeMilliTS);
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+RT_EXPORT_SYMBOL(RTTimeSystemNanoTS);
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+RT_EXPORT_SYMBOL(RTTimeSystemMilliTS);
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ IPRT_LINUX_SAVE_EFL_AC();
+#if RTLNX_VER_MIN(3,17,0)
+ struct timespec64 Ts;
+ ktime_get_real_ts64(&Ts); /* ktime_get_real_ts64 was added as a macro in 3.17, function since 4.18. */
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return RTTimeSpecSetTimespec64(pTime, &Ts);
+
+#elif RTLNX_VER_MIN(2,6,16)
+ struct timespec Ts;
+ ktime_get_real_ts(&Ts); /* ktime_get_real_ts was removed in Linux 4.20. */
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return RTTimeSpecSetTimespec(pTime, &Ts);
+
+#else /* < 2.6.16 */
+ struct timeval Tv;
+ do_gettimeofday(&Tv);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return RTTimeSpecSetTimeval(pTime, &Tv);
+#endif
+}
+RT_EXPORT_SYMBOL(RTTimeNow);
+
diff --git a/src/VBox/Runtime/r0drv/linux/timer-r0drv-linux.c b/src/VBox/Runtime/r0drv/linux/timer-r0drv-linux.c
new file mode 100644
index 00000000..68886a8c
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/timer-r0drv-linux.c
@@ -0,0 +1,1739 @@
+/* $Id: timer-r0drv-linux.c $ */
+/** @file
+ * IPRT - Timers, Ring-0 Driver, Linux.
+ */
+
+/*
+ * 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 "the-linux-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/timer.h>
+#include <iprt/time.h>
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/spinlock.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+
+#include "internal/magics.h"
+
+/** @def RTTIMER_LINUX_WITH_HRTIMER
+ * Whether to use high resolution timers. */
+#if !defined(RTTIMER_LINUX_WITH_HRTIMER) \
+ && defined(IPRT_LINUX_HAS_HRTIMER)
+# define RTTIMER_LINUX_WITH_HRTIMER
+#endif
+
+#if RTLNX_VER_MAX(2,6,31)
+# define mod_timer_pinned mod_timer
+# define HRTIMER_MODE_ABS_PINNED HRTIMER_MODE_ABS
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Timer state machine.
+ *
+ * This is used to try handle the issues with MP events and
+ * timers that runs on all CPUs. It's relatively nasty :-/
+ */
+typedef enum RTTIMERLNXSTATE
+{
+ /** Stopped. */
+ RTTIMERLNXSTATE_STOPPED = 0,
+ /** Transient state; next ACTIVE. */
+ RTTIMERLNXSTATE_STARTING,
+ /** Transient state; next ACTIVE. (not really necessary) */
+ RTTIMERLNXSTATE_MP_STARTING,
+ /** Active. */
+ RTTIMERLNXSTATE_ACTIVE,
+ /** Active and in callback; next ACTIVE, STOPPED or CALLBACK_DESTROYING. */
+ RTTIMERLNXSTATE_CALLBACK,
+ /** Stopped while in the callback; next STOPPED. */
+ RTTIMERLNXSTATE_CB_STOPPING,
+ /** Restarted while in the callback; next ACTIVE, STOPPED, DESTROYING. */
+ RTTIMERLNXSTATE_CB_RESTARTING,
+ /** The callback shall destroy the timer; next STOPPED. */
+ RTTIMERLNXSTATE_CB_DESTROYING,
+ /** Transient state; next STOPPED. */
+ RTTIMERLNXSTATE_STOPPING,
+ /** Transient state; next STOPPED. */
+ RTTIMERLNXSTATE_MP_STOPPING,
+ /** The usual 32-bit hack. */
+ RTTIMERLNXSTATE_32BIT_HACK = 0x7fffffff
+} RTTIMERLNXSTATE;
+
+
+/**
+ * A Linux sub-timer.
+ */
+typedef struct RTTIMERLNXSUBTIMER
+{
+ /** Timer specific data. */
+ union
+ {
+#if defined(RTTIMER_LINUX_WITH_HRTIMER)
+ /** High resolution timer. */
+ struct
+ {
+ /** The linux timer structure. */
+ struct hrtimer LnxTimer;
+ } Hr;
+#endif
+ /** Standard timer. */
+ struct
+ {
+ /** The linux timer structure. */
+ struct timer_list LnxTimer;
+ /** The start of the current run (ns).
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t u64NextTS;
+ /** When the timer was started. */
+ uint64_t nsStartTS;
+ /** The u64NextTS in jiffies. */
+ unsigned long ulNextJiffies;
+ /** Set when starting or changing the timer so that u64StartTs
+ * and u64NextTS gets reinitialized (eliminating some jitter). */
+ bool volatile fFirstAfterChg;
+ } Std;
+ } u;
+ /** The current tick number. */
+ uint64_t iTick;
+ /** Restart the single shot timer at this specific time.
+ * Used when a single shot timer is restarted from the callback. */
+ uint64_t volatile uNsRestartAt;
+ /** Pointer to the parent timer. */
+ PRTTIMER pParent;
+ /** The current sub-timer state. */
+ RTTIMERLNXSTATE volatile enmState;
+} RTTIMERLNXSUBTIMER;
+/** Pointer to a linux sub-timer. */
+typedef RTTIMERLNXSUBTIMER *PRTTIMERLNXSUBTIMER;
+
+
+/**
+ * The internal representation of an Linux timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Spinlock synchronizing the fSuspended and MP event handling.
+ * This is NIL_RTSPINLOCK if cCpus == 1. */
+ RTSPINLOCK hSpinlock;
+ /** Flag indicating that the timer is suspended. */
+ bool volatile fSuspended;
+ /** Whether the timer must run on one specific CPU or not. */
+ bool fSpecificCpu;
+#ifdef CONFIG_SMP
+ /** Whether the timer must run on all CPUs or not. */
+ bool fAllCpus;
+#endif /* else: All -> specific on non-SMP kernels */
+ /** Whether it is a high resolution timer or a standard one. */
+ bool fHighRes;
+ /** The id of the CPU it must run on if fSpecificCpu is set. */
+ RTCPUID idCpu;
+ /** The number of CPUs this timer should run on. */
+ RTCPUID cCpus;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** User argument. */
+ void *pvUser;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t volatile u64NanoInterval;
+ /** This is set to the number of jiffies between ticks if the interval is
+ * an exact number of jiffies. (Standard timers only.) */
+ unsigned long volatile cJiffies;
+ /** The change interval spinlock for standard timers only. */
+ spinlock_t ChgIntLock;
+ /** Workqueue item for delayed destruction. */
+ RTR0LNXWORKQUEUEITEM DtorWorkqueueItem;
+ /** Sub-timers.
+ * Normally there is just one, but for RTTIMER_FLAGS_CPU_ALL this will contain
+ * an entry for all possible cpus. In that case the index will be the same as
+ * for the RTCpuSet. */
+ RTTIMERLNXSUBTIMER aSubTimers[1];
+} RTTIMER;
+
+
+/**
+ * A rtTimerLinuxStartOnCpu and rtTimerLinuxStartOnCpu argument package.
+ */
+typedef struct RTTIMERLINUXSTARTONCPUARGS
+{
+ /** The current time (RTTimeSystemNanoTS). */
+ uint64_t u64Now;
+ /** When to start firing (delta). */
+ uint64_t u64First;
+} RTTIMERLINUXSTARTONCPUARGS;
+/** Pointer to a rtTimerLinuxStartOnCpu argument package. */
+typedef RTTIMERLINUXSTARTONCPUARGS *PRTTIMERLINUXSTARTONCPUARGS;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef CONFIG_SMP
+static DECLCALLBACK(void) rtTimerLinuxMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser);
+#endif
+
+#if 0
+#define DEBUG_HACKING
+#include <iprt/string.h>
+#include <iprt/asm-amd64-x86.h>
+static void myLogBackdoorPrintf(const char *pszFormat, ...)
+{
+ char szTmp[256];
+ va_list args;
+ size_t cb;
+
+ cb = RTStrPrintf(szTmp, sizeof(szTmp) - 10, "%d: ", RTMpCpuId());
+ va_start(args, pszFormat);
+ cb += RTStrPrintfV(&szTmp[cb], sizeof(szTmp) - cb, pszFormat, args);
+ va_end(args);
+
+ ASMOutStrU8(0x504, (uint8_t *)&szTmp[0], cb);
+}
+# define RTAssertMsg1Weak(pszExpr, uLine, pszFile, pszFunction) \
+ myLogBackdoorPrintf("\n!!Guest Assertion failed!!\n%s(%d) %s\n%s\n", uLine, pszFile, pszFunction, (pszExpr))
+# define RTAssertMsg2Weak myLogBackdoorPrintf
+# define RTTIMERLNX_LOG(a) myLogBackdoorPrintf a
+#else
+# define RTTIMERLNX_LOG(a) do { } while (0)
+#endif
+
+/**
+ * Sets the state.
+ */
+DECLINLINE(void) rtTimerLnxSetState(RTTIMERLNXSTATE volatile *penmState, RTTIMERLNXSTATE enmNewState)
+{
+#ifdef DEBUG_HACKING
+ RTTIMERLNX_LOG(("set %d -> %d\n", *penmState, enmNewState));
+#endif
+ ASMAtomicWriteU32((uint32_t volatile *)penmState, enmNewState);
+}
+
+
+/**
+ * Sets the state if it has a certain value.
+ *
+ * @return true if xchg was done.
+ * @return false if xchg wasn't done.
+ */
+#ifdef DEBUG_HACKING
+#define rtTimerLnxCmpXchgState(penmState, enmNewState, enmCurState) rtTimerLnxCmpXchgStateDebug(penmState, enmNewState, enmCurState, __LINE__)
+static bool rtTimerLnxCmpXchgStateDebug(RTTIMERLNXSTATE volatile *penmState, RTTIMERLNXSTATE enmNewState,
+ RTTIMERLNXSTATE enmCurState, uint32_t uLine)
+{
+ RTTIMERLNXSTATE enmOldState = enmCurState;
+ bool fRc = ASMAtomicCmpXchgExU32((uint32_t volatile *)penmState, enmNewState, enmCurState, (uint32_t *)&enmOldState);
+ RTTIMERLNX_LOG(("cxg %d -> %d - %d at %u\n", enmOldState, enmNewState, fRc, uLine));
+ return fRc;
+}
+#else
+DECLINLINE(bool) rtTimerLnxCmpXchgState(RTTIMERLNXSTATE volatile *penmState, RTTIMERLNXSTATE enmNewState,
+ RTTIMERLNXSTATE enmCurState)
+{
+ return ASMAtomicCmpXchgU32((uint32_t volatile *)penmState, enmNewState, enmCurState);
+}
+#endif
+
+
+/**
+ * Gets the state.
+ */
+DECLINLINE(RTTIMERLNXSTATE) rtTimerLnxGetState(RTTIMERLNXSTATE volatile *penmState)
+{
+ return (RTTIMERLNXSTATE)ASMAtomicUoReadU32((uint32_t volatile *)penmState);
+}
+
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+
+/**
+ * Converts a nano second time stamp to ktime_t.
+ *
+ * ASSUMES RTTimeSystemNanoTS() is implemented using ktime_get_ts().
+ *
+ * @returns ktime_t.
+ * @param cNanoSecs Nanoseconds.
+ */
+DECLINLINE(ktime_t) rtTimerLnxNanoToKt(uint64_t cNanoSecs)
+{
+ /* With some luck the compiler optimizes the division out of this... (Bet it doesn't.) */
+ return ktime_set(cNanoSecs / 1000000000, cNanoSecs % 1000000000);
+}
+
+/**
+ * Converts ktime_t to a nano second time stamp.
+ *
+ * ASSUMES RTTimeSystemNanoTS() is implemented using ktime_get_ts().
+ *
+ * @returns nano second time stamp.
+ * @param Kt ktime_t.
+ */
+DECLINLINE(uint64_t) rtTimerLnxKtToNano(ktime_t Kt)
+{
+ return ktime_to_ns(Kt);
+}
+
+#endif /* RTTIMER_LINUX_WITH_HRTIMER */
+
+/**
+ * Converts a nano second interval to jiffies.
+ *
+ * @returns Jiffies.
+ * @param cNanoSecs Nanoseconds.
+ */
+DECLINLINE(unsigned long) rtTimerLnxNanoToJiffies(uint64_t cNanoSecs)
+{
+ /* this can be made even better... */
+ if (cNanoSecs > (uint64_t)TICK_NSEC * MAX_JIFFY_OFFSET)
+ return MAX_JIFFY_OFFSET;
+# if ARCH_BITS == 32
+ if (RT_LIKELY(cNanoSecs <= UINT32_MAX))
+ return ((uint32_t)cNanoSecs + (TICK_NSEC-1)) / TICK_NSEC;
+# endif
+ return (cNanoSecs + (TICK_NSEC-1)) / TICK_NSEC;
+}
+
+
+/**
+ * Starts a sub-timer (RTTimerStart).
+ *
+ * @param pSubTimer The sub-timer to start.
+ * @param u64Now The current timestamp (RTTimeSystemNanoTS()).
+ * @param u64First The interval from u64Now to the first time the timer should fire.
+ * @param fPinned true = timer pinned to a specific CPU,
+ * false = timer can migrate between CPUs
+ * @param fHighRes Whether the user requested a high resolution timer or not.
+ * @param enmOldState The old timer state.
+ */
+static void rtTimerLnxStartSubTimer(PRTTIMERLNXSUBTIMER pSubTimer, uint64_t u64Now, uint64_t u64First,
+ bool fPinned, bool fHighRes)
+{
+ /*
+ * Calc when it should start firing.
+ */
+ uint64_t u64NextTS = u64Now + u64First;
+ if (!fHighRes)
+ {
+ pSubTimer->u.Std.u64NextTS = u64NextTS;
+ pSubTimer->u.Std.nsStartTS = u64NextTS;
+ }
+ RTTIMERLNX_LOG(("startsubtimer %p\n", pSubTimer->pParent));
+
+ pSubTimer->iTick = 0;
+
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+ if (fHighRes)
+ hrtimer_start(&pSubTimer->u.Hr.LnxTimer, rtTimerLnxNanoToKt(u64NextTS),
+ fPinned ? HRTIMER_MODE_ABS_PINNED : HRTIMER_MODE_ABS);
+ else
+#endif
+ {
+ unsigned long cJiffies = !u64First ? 0 : rtTimerLnxNanoToJiffies(u64First);
+ pSubTimer->u.Std.ulNextJiffies = jiffies + cJiffies;
+ pSubTimer->u.Std.fFirstAfterChg = true;
+#ifdef CONFIG_SMP
+ if (fPinned)
+ {
+# if RTLNX_VER_MIN(4,8,0)
+ mod_timer(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
+# else
+ mod_timer_pinned(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
+# endif
+ }
+ else
+#endif
+ mod_timer(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
+ }
+
+ /* Be a bit careful here since we could be racing the callback. */
+ if (!rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_STARTING))
+ rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_MP_STARTING);
+}
+
+
+/**
+ * Stops a sub-timer (RTTimerStart and rtTimerLinuxMpEvent()).
+ *
+ * The caller has already changed the state, so we will not be in a callback
+ * situation wrt to the calling thread.
+ *
+ * @param pSubTimer The sub-timer.
+ * @param fHighRes Whether the user requested a high resolution timer or not.
+ */
+static void rtTimerLnxStopSubTimer(PRTTIMERLNXSUBTIMER pSubTimer, bool fHighRes)
+{
+ RTTIMERLNX_LOG(("stopsubtimer %p %d\n", pSubTimer->pParent, fHighRes));
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+ if (fHighRes)
+ {
+ /* There is no equivalent to del_timer in the hrtimer API,
+ hrtimer_cancel() == del_timer_sync(). Just like the WARN_ON in
+ del_timer_sync() asserts, waiting for a timer callback to complete
+ is deadlock prone, so don't do it. */
+ int rc = hrtimer_try_to_cancel(&pSubTimer->u.Hr.LnxTimer);
+ if (rc < 0)
+ {
+ hrtimer_start(&pSubTimer->u.Hr.LnxTimer, ktime_set(KTIME_SEC_MAX, 0), HRTIMER_MODE_ABS);
+ hrtimer_try_to_cancel(&pSubTimer->u.Hr.LnxTimer);
+ }
+ }
+ else
+#endif
+ del_timer(&pSubTimer->u.Std.LnxTimer);
+
+ rtTimerLnxSetState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED);
+}
+
+
+/**
+ * Used by RTTimerDestroy and rtTimerLnxCallbackDestroy to do the actual work.
+ *
+ * @param pTimer The timer in question.
+ */
+static void rtTimerLnxDestroyIt(PRTTIMER pTimer)
+{
+ RTSPINLOCK hSpinlock = pTimer->hSpinlock;
+ RTCPUID iCpu;
+ Assert(pTimer->fSuspended);
+ RTTIMERLNX_LOG(("destroyit %p\n", pTimer));
+
+ /*
+ * Remove the MP notifications first because it'll reduce the risk of
+ * us overtaking any MP event that might theoretically be racing us here.
+ */
+#ifdef CONFIG_SMP
+ if ( pTimer->cCpus > 1
+ && hSpinlock != NIL_RTSPINLOCK)
+ {
+ int rc = RTMpNotificationDeregister(rtTimerLinuxMpEvent, pTimer);
+ AssertRC(rc);
+ }
+#endif /* CONFIG_SMP */
+
+ /*
+ * Invalidate the handle.
+ */
+ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+
+ /*
+ * Make sure all timers have stopped executing since we're stopping them in
+ * an asynchronous manner up in rtTimerLnxStopSubTimer.
+ */
+ iCpu = pTimer->cCpus;
+ while (iCpu-- > 0)
+ {
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+ if (pTimer->fHighRes)
+ hrtimer_cancel(&pTimer->aSubTimers[iCpu].u.Hr.LnxTimer);
+ else
+#endif
+ del_timer_sync(&pTimer->aSubTimers[iCpu].u.Std.LnxTimer);
+ }
+
+ /*
+ * Finally, free the resources.
+ */
+ RTMemFreeEx(pTimer, RT_UOFFSETOF_DYN(RTTIMER, aSubTimers[pTimer->cCpus]));
+ if (hSpinlock != NIL_RTSPINLOCK)
+ RTSpinlockDestroy(hSpinlock);
+}
+
+
+/**
+ * Workqueue callback (no DECLCALLBACK!) for deferred destruction.
+ *
+ * @param pWork Pointer to the DtorWorkqueueItem member of our timer
+ * structure.
+ */
+static void rtTimerLnxDestroyDeferred(RTR0LNXWORKQUEUEITEM *pWork)
+{
+ PRTTIMER pTimer = RT_FROM_MEMBER(pWork, RTTIMER, DtorWorkqueueItem);
+ rtTimerLnxDestroyIt(pTimer);
+}
+
+
+/**
+ * Called when the timer was destroyed by the callback function.
+ *
+ * @param pTimer The timer.
+ * @param pSubTimer The sub-timer which we're handling, the state of this
+ * will be RTTIMERLNXSTATE_CALLBACK_DESTROYING.
+ */
+static void rtTimerLnxCallbackDestroy(PRTTIMER pTimer, PRTTIMERLNXSUBTIMER pSubTimer)
+{
+ /*
+ * If it's an omni timer, the last dude does the destroying.
+ */
+ if (pTimer->cCpus > 1)
+ {
+ uint32_t iCpu = pTimer->cCpus;
+ RTSpinlockAcquire(pTimer->hSpinlock);
+
+ Assert(pSubTimer->enmState == RTTIMERLNXSTATE_CB_DESTROYING);
+ rtTimerLnxSetState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED);
+
+ while (iCpu-- > 0)
+ if (rtTimerLnxGetState(&pTimer->aSubTimers[iCpu].enmState) != RTTIMERLNXSTATE_STOPPED)
+ {
+ RTSpinlockRelease(pTimer->hSpinlock);
+ return;
+ }
+
+ RTSpinlockRelease(pTimer->hSpinlock);
+ }
+
+ /*
+ * Destroying a timer from the callback is unsafe since the callout code
+ * might be touching the timer structure upon return (hrtimer does!). So,
+ * we have to defer the actual destruction to the IRPT workqueue.
+ */
+ rtR0LnxWorkqueuePush(&pTimer->DtorWorkqueueItem, rtTimerLnxDestroyDeferred);
+}
+
+
+#ifdef CONFIG_SMP
+/**
+ * Deal with a sub-timer that has migrated.
+ *
+ * @param pTimer The timer.
+ * @param pSubTimer The sub-timer.
+ */
+static void rtTimerLnxCallbackHandleMigration(PRTTIMER pTimer, PRTTIMERLNXSUBTIMER pSubTimer)
+{
+ RTTIMERLNXSTATE enmState;
+ if (pTimer->cCpus > 1)
+ RTSpinlockAcquire(pTimer->hSpinlock);
+
+ do
+ {
+ enmState = rtTimerLnxGetState(&pSubTimer->enmState);
+ switch (enmState)
+ {
+ case RTTIMERLNXSTATE_STOPPING:
+ case RTTIMERLNXSTATE_MP_STOPPING:
+ enmState = RTTIMERLNXSTATE_STOPPED;
+ RT_FALL_THRU();
+ case RTTIMERLNXSTATE_STOPPED:
+ break;
+
+ default:
+ AssertMsgFailed(("%d\n", enmState));
+ RT_FALL_THRU();
+ case RTTIMERLNXSTATE_STARTING:
+ case RTTIMERLNXSTATE_MP_STARTING:
+ case RTTIMERLNXSTATE_ACTIVE:
+ case RTTIMERLNXSTATE_CALLBACK:
+ case RTTIMERLNXSTATE_CB_STOPPING:
+ case RTTIMERLNXSTATE_CB_RESTARTING:
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, enmState))
+ enmState = RTTIMERLNXSTATE_STOPPED;
+ break;
+
+ case RTTIMERLNXSTATE_CB_DESTROYING:
+ {
+ if (pTimer->cCpus > 1)
+ RTSpinlockRelease(pTimer->hSpinlock);
+
+ rtTimerLnxCallbackDestroy(pTimer, pSubTimer);
+ return;
+ }
+ }
+ } while (enmState != RTTIMERLNXSTATE_STOPPED);
+
+ if (pTimer->cCpus > 1)
+ RTSpinlockRelease(pTimer->hSpinlock);
+}
+#endif /* CONFIG_SMP */
+
+
+/**
+ * The slow path of rtTimerLnxChangeToCallbackState.
+ *
+ * @returns true if changed successfully, false if not.
+ * @param pSubTimer The sub-timer.
+ */
+static bool rtTimerLnxChangeToCallbackStateSlow(PRTTIMERLNXSUBTIMER pSubTimer)
+{
+ for (;;)
+ {
+ RTTIMERLNXSTATE enmState = rtTimerLnxGetState(&pSubTimer->enmState);
+ switch (enmState)
+ {
+ case RTTIMERLNXSTATE_ACTIVE:
+ case RTTIMERLNXSTATE_STARTING:
+ case RTTIMERLNXSTATE_MP_STARTING:
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_CALLBACK, enmState))
+ return true;
+ break;
+
+ case RTTIMERLNXSTATE_CALLBACK:
+ case RTTIMERLNXSTATE_CB_STOPPING:
+ case RTTIMERLNXSTATE_CB_RESTARTING:
+ case RTTIMERLNXSTATE_CB_DESTROYING:
+ AssertMsgFailed(("%d\n", enmState)); RT_FALL_THRU();
+ default:
+ return false;
+ }
+ ASMNopPause();
+ }
+}
+
+
+/**
+ * Tries to change the sub-timer state to 'callback'.
+ *
+ * @returns true if changed successfully, false if not.
+ * @param pSubTimer The sub-timer.
+ */
+DECLINLINE(bool) rtTimerLnxChangeToCallbackState(PRTTIMERLNXSUBTIMER pSubTimer)
+{
+ if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_CALLBACK, RTTIMERLNXSTATE_ACTIVE)))
+ return true;
+ return rtTimerLnxChangeToCallbackStateSlow(pSubTimer);
+}
+
+
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+/**
+ * Timer callback function for high resolution timers.
+ *
+ * @returns HRTIMER_NORESTART or HRTIMER_RESTART depending on whether it's a
+ * one-shot or interval timer.
+ * @param pHrTimer Pointer to the sub-timer structure.
+ */
+static enum hrtimer_restart rtTimerLinuxHrCallback(struct hrtimer *pHrTimer)
+{
+ PRTTIMERLNXSUBTIMER pSubTimer = RT_FROM_MEMBER(pHrTimer, RTTIMERLNXSUBTIMER, u.Hr.LnxTimer);
+ PRTTIMER pTimer = pSubTimer->pParent;
+
+
+ RTTIMERLNX_LOG(("hrcallback %p\n", pTimer));
+ if (RT_UNLIKELY(!rtTimerLnxChangeToCallbackState(pSubTimer)))
+ return HRTIMER_NORESTART;
+
+#ifdef CONFIG_SMP
+ /*
+ * Check for unwanted migration.
+ */
+ if (pTimer->fAllCpus || pTimer->fSpecificCpu)
+ {
+ RTCPUID idCpu = RTMpCpuId();
+ if (RT_UNLIKELY( pTimer->fAllCpus
+ ? (RTCPUID)(pSubTimer - &pTimer->aSubTimers[0]) != idCpu
+ : pTimer->idCpu != idCpu))
+ {
+ rtTimerLnxCallbackHandleMigration(pTimer, pSubTimer);
+ return HRTIMER_NORESTART;
+ }
+ }
+#endif
+
+ if (pTimer->u64NanoInterval)
+ {
+ /*
+ * Periodic timer, run it and update the native timer afterwards so
+ * we can handle RTTimerStop and RTTimerChangeInterval from the
+ * callback as well as a racing control thread.
+ */
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
+ hrtimer_add_expires_ns(&pSubTimer->u.Hr.LnxTimer, ASMAtomicReadU64(&pTimer->u64NanoInterval));
+ if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CALLBACK)))
+ return HRTIMER_RESTART;
+ }
+ else
+ {
+ /*
+ * One shot timer (no omni), stop it before dispatching it.
+ * Allow RTTimerStart as well as RTTimerDestroy to be called from
+ * the callback.
+ */
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
+ if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CALLBACK)))
+ return HRTIMER_NORESTART;
+ }
+
+ /*
+ * Some state change occurred while we were in the callback routine.
+ */
+ for (;;)
+ {
+ RTTIMERLNXSTATE enmState = rtTimerLnxGetState(&pSubTimer->enmState);
+ switch (enmState)
+ {
+ case RTTIMERLNXSTATE_CB_DESTROYING:
+ rtTimerLnxCallbackDestroy(pTimer, pSubTimer);
+ return HRTIMER_NORESTART;
+
+ case RTTIMERLNXSTATE_CB_STOPPING:
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CB_STOPPING))
+ return HRTIMER_NORESTART;
+ break;
+
+ case RTTIMERLNXSTATE_CB_RESTARTING:
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CB_RESTARTING))
+ {
+ pSubTimer->iTick = 0;
+ hrtimer_set_expires(&pSubTimer->u.Hr.LnxTimer, rtTimerLnxNanoToKt(pSubTimer->uNsRestartAt));
+ return HRTIMER_RESTART;
+ }
+ break;
+
+ default:
+ AssertMsgFailed(("%d\n", enmState));
+ return HRTIMER_NORESTART;
+ }
+ ASMNopPause();
+ }
+}
+#endif /* RTTIMER_LINUX_WITH_HRTIMER */
+
+
+#if RTLNX_VER_MIN(4,15,0)
+/**
+ * Timer callback function for standard timers.
+ *
+ * @param pLnxTimer Pointer to the Linux timer structure.
+ */
+static void rtTimerLinuxStdCallback(struct timer_list *pLnxTimer)
+{
+ PRTTIMERLNXSUBTIMER pSubTimer = from_timer(pSubTimer, pLnxTimer, u.Std.LnxTimer);
+#else
+/**
+ * Timer callback function for standard timers.
+ *
+ * @param ulUser Address of the sub-timer structure.
+ */
+static void rtTimerLinuxStdCallback(unsigned long ulUser)
+{
+ PRTTIMERLNXSUBTIMER pSubTimer = (PRTTIMERLNXSUBTIMER)ulUser;
+#endif
+ PRTTIMER pTimer = pSubTimer->pParent;
+
+ RTTIMERLNX_LOG(("stdcallback %p\n", pTimer));
+ if (RT_UNLIKELY(!rtTimerLnxChangeToCallbackState(pSubTimer)))
+ return;
+
+#ifdef CONFIG_SMP
+ /*
+ * Check for unwanted migration.
+ */
+ if (pTimer->fAllCpus || pTimer->fSpecificCpu)
+ {
+ RTCPUID idCpu = RTMpCpuId();
+ if (RT_UNLIKELY( pTimer->fAllCpus
+ ? (RTCPUID)(pSubTimer - &pTimer->aSubTimers[0]) != idCpu
+ : pTimer->idCpu != idCpu))
+ {
+ rtTimerLnxCallbackHandleMigration(pTimer, pSubTimer);
+ return;
+ }
+ }
+#endif
+
+ if (pTimer->u64NanoInterval)
+ {
+ /*
+ * Interval timer, calculate the next timeout.
+ *
+ * The first time around, we'll re-adjust the u.Std.u64NextTS to
+ * try prevent some jittering if we were started at a bad time.
+ */
+ const uint64_t iTick = ++pSubTimer->iTick;
+ unsigned long uCurJiffies = jiffies;
+ unsigned long ulNextJiffies;
+ uint64_t u64NanoInterval;
+ unsigned long cJiffies;
+ unsigned long flFlags;
+
+ spin_lock_irqsave(&pTimer->ChgIntLock, flFlags);
+ u64NanoInterval = pTimer->u64NanoInterval;
+ cJiffies = pTimer->cJiffies;
+ if (RT_UNLIKELY(pSubTimer->u.Std.fFirstAfterChg))
+ {
+ pSubTimer->u.Std.fFirstAfterChg = false;
+ pSubTimer->u.Std.u64NextTS = RTTimeSystemNanoTS();
+ pSubTimer->u.Std.nsStartTS = pSubTimer->u.Std.u64NextTS - u64NanoInterval * (iTick - 1);
+ pSubTimer->u.Std.ulNextJiffies = uCurJiffies = jiffies;
+ }
+ spin_unlock_irqrestore(&pTimer->ChgIntLock, flFlags);
+
+ pSubTimer->u.Std.u64NextTS += u64NanoInterval;
+ if (cJiffies)
+ {
+ ulNextJiffies = pSubTimer->u.Std.ulNextJiffies + cJiffies;
+ pSubTimer->u.Std.ulNextJiffies = ulNextJiffies;
+ if (time_after_eq(ulNextJiffies, uCurJiffies))
+ { /* likely */ }
+ else
+ {
+ unsigned long cJiffiesBehind = uCurJiffies - ulNextJiffies;
+ ulNextJiffies = uCurJiffies + cJiffies / 2;
+ if (cJiffiesBehind >= HZ / 4) /* Conside if we're lagging too far behind. Screw the u64NextTS member. */
+ pSubTimer->u.Std.ulNextJiffies = ulNextJiffies;
+ /*else: Don't update u.Std.ulNextJiffies so we can continue catching up in the next tick. */
+ }
+ }
+ else
+ {
+ const uint64_t u64NanoTS = RTTimeSystemNanoTS();
+ const int64_t cNsBehind = u64NanoTS - pSubTimer->u.Std.u64NextTS;
+ if (cNsBehind <= 0)
+ ulNextJiffies = uCurJiffies + rtTimerLnxNanoToJiffies(pSubTimer->u.Std.u64NextTS - u64NanoTS);
+ else if (u64NanoInterval >= RT_NS_1SEC_64 * 2 / HZ)
+ {
+ ulNextJiffies = uCurJiffies + rtTimerLnxNanoToJiffies(u64NanoInterval / 2);
+ if (cNsBehind >= RT_NS_1SEC_64 / HZ / 4) /* Conside if we're lagging too far behind. */
+ pSubTimer->u.Std.u64NextTS = u64NanoTS + u64NanoInterval / 2;
+ }
+ else
+ {
+ ulNextJiffies = uCurJiffies + 1;
+ if (cNsBehind >= RT_NS_1SEC_64 / HZ / 4) /* Conside if we're lagging too far behind. */
+ pSubTimer->u.Std.u64NextTS = u64NanoTS + RT_NS_1SEC_64 / HZ;
+ }
+ pSubTimer->u.Std.ulNextJiffies = ulNextJiffies;
+ }
+
+ /*
+ * Run the timer and re-arm it unless the state changed .
+ * .
+ * We must re-arm it afterwards as we're not in a position to undo this .
+ * operation if for instance someone stopped or destroyed us while we .
+ * were in the callback. (Linux takes care of any races here.)
+ */
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, iTick);
+ if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CALLBACK)))
+ {
+#ifdef CONFIG_SMP
+ if (pTimer->fSpecificCpu || pTimer->fAllCpus)
+ {
+# if RTLNX_VER_MIN(4,8,0)
+ mod_timer(&pSubTimer->u.Std.LnxTimer, ulNextJiffies);
+# else
+ mod_timer_pinned(&pSubTimer->u.Std.LnxTimer, ulNextJiffies);
+# endif
+ }
+ else
+#endif
+ mod_timer(&pSubTimer->u.Std.LnxTimer, ulNextJiffies);
+ return;
+ }
+ }
+ else
+ {
+ /*
+ * One shot timer, stop it before dispatching it.
+ * Allow RTTimerStart as well as RTTimerDestroy to be called from
+ * the callback.
+ */
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
+ if (RT_LIKELY(rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CALLBACK)))
+ return;
+ }
+
+ /*
+ * Some state change occurred while we were in the callback routine.
+ */
+ for (;;)
+ {
+ RTTIMERLNXSTATE enmState = rtTimerLnxGetState(&pSubTimer->enmState);
+ switch (enmState)
+ {
+ case RTTIMERLNXSTATE_CB_DESTROYING:
+ rtTimerLnxCallbackDestroy(pTimer, pSubTimer);
+ return;
+
+ case RTTIMERLNXSTATE_CB_STOPPING:
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_CB_STOPPING))
+ return;
+ break;
+
+ case RTTIMERLNXSTATE_CB_RESTARTING:
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_ACTIVE, RTTIMERLNXSTATE_CB_RESTARTING))
+ {
+ uint64_t u64NanoTS;
+ uint64_t u64NextTS;
+ unsigned long flFlags;
+
+ spin_lock_irqsave(&pTimer->ChgIntLock, flFlags);
+ u64NextTS = pSubTimer->uNsRestartAt;
+ u64NanoTS = RTTimeSystemNanoTS();
+ pSubTimer->iTick = 0;
+ pSubTimer->u.Std.u64NextTS = u64NextTS;
+ pSubTimer->u.Std.fFirstAfterChg = true;
+ pSubTimer->u.Std.ulNextJiffies = u64NextTS > u64NanoTS
+ ? jiffies + rtTimerLnxNanoToJiffies(u64NextTS - u64NanoTS)
+ : jiffies;
+ spin_unlock_irqrestore(&pTimer->ChgIntLock, flFlags);
+
+#ifdef CONFIG_SMP
+ if (pTimer->fSpecificCpu || pTimer->fAllCpus)
+ {
+# if RTLNX_VER_MIN(4,8,0)
+ mod_timer(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
+# else
+ mod_timer_pinned(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
+# endif
+ }
+ else
+#endif
+ mod_timer(&pSubTimer->u.Std.LnxTimer, pSubTimer->u.Std.ulNextJiffies);
+ return;
+ }
+ break;
+
+ default:
+ AssertMsgFailed(("%d\n", enmState));
+ return;
+ }
+ ASMNopPause();
+ }
+}
+
+
+#ifdef CONFIG_SMP
+
+/**
+ * Per-cpu callback function (RTMpOnAll/RTMpOnSpecific).
+ *
+ * @param idCpu The current CPU.
+ * @param pvUser1 Pointer to the timer.
+ * @param pvUser2 Pointer to the argument structure.
+ */
+static DECLCALLBACK(void) rtTimerLnxStartAllOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PRTTIMERLINUXSTARTONCPUARGS pArgs = (PRTTIMERLINUXSTARTONCPUARGS)pvUser2;
+ PRTTIMER pTimer = (PRTTIMER)pvUser1;
+ Assert(idCpu < pTimer->cCpus);
+ rtTimerLnxStartSubTimer(&pTimer->aSubTimers[idCpu], pArgs->u64Now, pArgs->u64First, true /*fPinned*/, pTimer->fHighRes);
+}
+
+
+/**
+ * Worker for RTTimerStart() that takes care of the ugly bits.
+ *
+ * @returns RTTimerStart() return value.
+ * @param pTimer The timer.
+ * @param pArgs The argument structure.
+ */
+static int rtTimerLnxOmniStart(PRTTIMER pTimer, PRTTIMERLINUXSTARTONCPUARGS pArgs)
+{
+ RTCPUID iCpu;
+ RTCPUSET OnlineSet;
+ RTCPUSET OnlineSet2;
+ int rc2;
+
+ /*
+ * Prepare all the sub-timers for the startup and then flag the timer
+ * as a whole as non-suspended, make sure we get them all before
+ * clearing fSuspended as the MP handler will be waiting on this
+ * should something happen while we're looping.
+ */
+ RTSpinlockAcquire(pTimer->hSpinlock);
+
+ /* Just make it a omni timer restriction that no stop/start races are allowed. */
+ for (iCpu = 0; iCpu < pTimer->cCpus; iCpu++)
+ if (rtTimerLnxGetState(&pTimer->aSubTimers[iCpu].enmState) != RTTIMERLNXSTATE_STOPPED)
+ {
+ RTSpinlockRelease(pTimer->hSpinlock);
+ return VERR_TIMER_BUSY;
+ }
+
+ do
+ {
+ RTMpGetOnlineSet(&OnlineSet);
+ for (iCpu = 0; iCpu < pTimer->cCpus; iCpu++)
+ {
+ Assert(pTimer->aSubTimers[iCpu].enmState != RTTIMERLNXSTATE_MP_STOPPING);
+ rtTimerLnxSetState(&pTimer->aSubTimers[iCpu].enmState,
+ RTCpuSetIsMember(&OnlineSet, iCpu)
+ ? RTTIMERLNXSTATE_STARTING
+ : RTTIMERLNXSTATE_STOPPED);
+ }
+ } while (!RTCpuSetIsEqual(&OnlineSet, RTMpGetOnlineSet(&OnlineSet2)));
+
+ ASMAtomicWriteBool(&pTimer->fSuspended, false);
+
+ RTSpinlockRelease(pTimer->hSpinlock);
+
+ /*
+ * Start them (can't find any exported function that allows me to
+ * do this without the cross calls).
+ */
+ pArgs->u64Now = RTTimeSystemNanoTS();
+ rc2 = RTMpOnAll(rtTimerLnxStartAllOnCpu, pTimer, pArgs);
+ AssertRC(rc2); /* screw this if it fails. */
+
+ /*
+ * Reset the sub-timers who didn't start up (ALL CPUs case).
+ */
+ RTSpinlockAcquire(pTimer->hSpinlock);
+
+ for (iCpu = 0; iCpu < pTimer->cCpus; iCpu++)
+ if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[iCpu].enmState, RTTIMERLNXSTATE_STOPPED, RTTIMERLNXSTATE_STARTING))
+ {
+ /** @todo very odd case for a rainy day. Cpus that temporarily went offline while
+ * we were between calls needs to nudged as the MP handler will ignore events for
+ * them because of the STARTING state. This is an extremely unlikely case - not that
+ * that means anything in my experience... ;-) */
+ RTTIMERLNX_LOG(("what!? iCpu=%u -> didn't start\n", iCpu));
+ }
+
+ RTSpinlockRelease(pTimer->hSpinlock);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTTimerStop() that takes care of the ugly SMP bits.
+ *
+ * @returns true if there was any active callbacks, false if not.
+ * @param pTimer The timer (valid).
+ * @param fForDestroy Whether this is for RTTimerDestroy or not.
+ */
+static bool rtTimerLnxOmniStop(PRTTIMER pTimer, bool fForDestroy)
+{
+ bool fActiveCallbacks = false;
+ RTCPUID iCpu;
+ RTTIMERLNXSTATE enmState;
+
+
+ /*
+ * Mark the timer as suspended and flag all timers as stopping, except
+ * for those being stopped by an MP event.
+ */
+ RTSpinlockAcquire(pTimer->hSpinlock);
+
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+ for (iCpu = 0; iCpu < pTimer->cCpus; iCpu++)
+ {
+ for (;;)
+ {
+ enmState = rtTimerLnxGetState(&pTimer->aSubTimers[iCpu].enmState);
+ if ( enmState == RTTIMERLNXSTATE_STOPPED
+ || enmState == RTTIMERLNXSTATE_MP_STOPPING)
+ break;
+ if ( enmState == RTTIMERLNXSTATE_CALLBACK
+ || enmState == RTTIMERLNXSTATE_CB_STOPPING
+ || enmState == RTTIMERLNXSTATE_CB_RESTARTING)
+ {
+ Assert(enmState != RTTIMERLNXSTATE_CB_STOPPING || fForDestroy);
+ if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[iCpu].enmState,
+ !fForDestroy ? RTTIMERLNXSTATE_CB_STOPPING : RTTIMERLNXSTATE_CB_DESTROYING,
+ enmState))
+ {
+ fActiveCallbacks = true;
+ break;
+ }
+ }
+ else
+ {
+ Assert(enmState == RTTIMERLNXSTATE_ACTIVE);
+ if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[iCpu].enmState, RTTIMERLNXSTATE_STOPPING, enmState))
+ break;
+ }
+ ASMNopPause();
+ }
+ }
+
+ RTSpinlockRelease(pTimer->hSpinlock);
+
+ /*
+ * Do the actual stopping. Fortunately, this doesn't require any IPIs.
+ * Unfortunately it cannot be done synchronously.
+ */
+ for (iCpu = 0; iCpu < pTimer->cCpus; iCpu++)
+ if (rtTimerLnxGetState(&pTimer->aSubTimers[iCpu].enmState) == RTTIMERLNXSTATE_STOPPING)
+ rtTimerLnxStopSubTimer(&pTimer->aSubTimers[iCpu], pTimer->fHighRes);
+
+ return fActiveCallbacks;
+}
+
+
+/**
+ * Per-cpu callback function (RTMpOnSpecific) used by rtTimerLinuxMpEvent()
+ * to start a sub-timer on a cpu that just have come online.
+ *
+ * @param idCpu The current CPU.
+ * @param pvUser1 Pointer to the timer.
+ * @param pvUser2 Pointer to the argument structure.
+ */
+static DECLCALLBACK(void) rtTimerLinuxMpStartOnCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PRTTIMERLINUXSTARTONCPUARGS pArgs = (PRTTIMERLINUXSTARTONCPUARGS)pvUser2;
+ PRTTIMER pTimer = (PRTTIMER)pvUser1;
+ RTSPINLOCK hSpinlock;
+ Assert(idCpu < pTimer->cCpus);
+
+ /*
+ * We have to be kind of careful here as we might be racing RTTimerStop
+ * (and/or RTTimerDestroy, thus the paranoia.
+ */
+ hSpinlock = pTimer->hSpinlock;
+ if ( hSpinlock != NIL_RTSPINLOCK
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ RTSpinlockAcquire(hSpinlock);
+
+ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ /* We're sane and the timer is not suspended yet. */
+ PRTTIMERLNXSUBTIMER pSubTimer = &pTimer->aSubTimers[idCpu];
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_MP_STARTING, RTTIMERLNXSTATE_STOPPED))
+ rtTimerLnxStartSubTimer(pSubTimer, pArgs->u64Now, pArgs->u64First, true /*fPinned*/, pTimer->fHighRes);
+ }
+
+ RTSpinlockRelease(hSpinlock);
+ }
+}
+
+
+/**
+ * MP event notification callback.
+ *
+ * @param enmEvent The event.
+ * @param idCpu The cpu it applies to.
+ * @param pvUser The timer.
+ */
+static DECLCALLBACK(void) rtTimerLinuxMpEvent(RTMPEVENT enmEvent, RTCPUID idCpu, void *pvUser)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvUser;
+ PRTTIMERLNXSUBTIMER pSubTimer = &pTimer->aSubTimers[idCpu];
+ RTSPINLOCK hSpinlock;
+
+ Assert(idCpu < pTimer->cCpus);
+
+ /*
+ * Some initial paranoia.
+ */
+ if (pTimer->u32Magic != RTTIMER_MAGIC)
+ return;
+ hSpinlock = pTimer->hSpinlock;
+ if (hSpinlock == NIL_RTSPINLOCK)
+ return;
+
+ RTSpinlockAcquire(hSpinlock);
+
+ /* Is it active? */
+ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ switch (enmEvent)
+ {
+ /*
+ * Try do it without leaving the spin lock, but if we have to, retake it
+ * when we're on the right cpu.
+ */
+ case RTMPEVENT_ONLINE:
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_MP_STARTING, RTTIMERLNXSTATE_STOPPED))
+ {
+ RTTIMERLINUXSTARTONCPUARGS Args;
+ Args.u64Now = RTTimeSystemNanoTS();
+ Args.u64First = 0;
+
+ if (RTMpCpuId() == idCpu)
+ rtTimerLnxStartSubTimer(pSubTimer, Args.u64Now, Args.u64First, true /*fPinned*/, pTimer->fHighRes);
+ else
+ {
+ rtTimerLnxSetState(&pSubTimer->enmState, RTTIMERLNXSTATE_STOPPED); /* we'll recheck it. */
+ RTSpinlockRelease(hSpinlock);
+
+ RTMpOnSpecific(idCpu, rtTimerLinuxMpStartOnCpu, pTimer, &Args);
+ return; /* we've left the spinlock */
+ }
+ }
+ break;
+
+ /*
+ * The CPU is (going) offline, make sure the sub-timer is stopped.
+ *
+ * Linux will migrate it to a different CPU, but we don't want this. The
+ * timer function is checking for this.
+ */
+ case RTMPEVENT_OFFLINE:
+ {
+ RTTIMERLNXSTATE enmState;
+ while ( (enmState = rtTimerLnxGetState(&pSubTimer->enmState)) == RTTIMERLNXSTATE_ACTIVE
+ || enmState == RTTIMERLNXSTATE_CALLBACK
+ || enmState == RTTIMERLNXSTATE_CB_RESTARTING)
+ {
+ if (enmState == RTTIMERLNXSTATE_ACTIVE)
+ {
+ if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_MP_STOPPING, RTTIMERLNXSTATE_ACTIVE))
+ {
+ RTSpinlockRelease(hSpinlock);
+
+ rtTimerLnxStopSubTimer(pSubTimer, pTimer->fHighRes);
+ return; /* we've left the spinlock */
+ }
+ }
+ else if (rtTimerLnxCmpXchgState(&pSubTimer->enmState, RTTIMERLNXSTATE_CB_STOPPING, enmState))
+ break;
+
+ /* State not stable, try again. */
+ ASMNopPause();
+ }
+ break;
+ }
+ }
+ }
+
+ RTSpinlockRelease(hSpinlock);
+}
+
+#endif /* CONFIG_SMP */
+
+
+/**
+ * Callback function use by RTTimerStart via RTMpOnSpecific to start a timer
+ * running on a specific CPU.
+ *
+ * @param idCpu The current CPU.
+ * @param pvUser1 Pointer to the timer.
+ * @param pvUser2 Pointer to the argument structure.
+ */
+static DECLCALLBACK(void) rtTimerLnxStartOnSpecificCpu(RTCPUID idCpu, void *pvUser1, void *pvUser2)
+{
+ PRTTIMERLINUXSTARTONCPUARGS pArgs = (PRTTIMERLINUXSTARTONCPUARGS)pvUser2;
+ PRTTIMER pTimer = (PRTTIMER)pvUser1;
+ RT_NOREF_PV(idCpu);
+ rtTimerLnxStartSubTimer(&pTimer->aSubTimers[0], pArgs->u64Now, pArgs->u64First, true /*fPinned*/, pTimer->fHighRes);
+}
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ RTTIMERLINUXSTARTONCPUARGS Args;
+ int rc2;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+
+ if (!ASMAtomicUoReadBool(&pTimer->fSuspended))
+ return VERR_TIMER_ACTIVE;
+ RTTIMERLNX_LOG(("start %p cCpus=%d\n", pTimer, pTimer->cCpus));
+
+ Args.u64First = u64First;
+#ifdef CONFIG_SMP
+ /*
+ * Omni timer?
+ */
+ if (pTimer->fAllCpus)
+ {
+ rc2 = rtTimerLnxOmniStart(pTimer, &Args);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc2;
+ }
+#endif
+
+ /*
+ * Simple timer - Pretty straight forward if it wasn't for restarting.
+ */
+ Args.u64Now = RTTimeSystemNanoTS();
+ ASMAtomicWriteU64(&pTimer->aSubTimers[0].uNsRestartAt, Args.u64Now + u64First);
+ for (;;)
+ {
+ RTTIMERLNXSTATE enmState = rtTimerLnxGetState(&pTimer->aSubTimers[0].enmState);
+ switch (enmState)
+ {
+ case RTTIMERLNXSTATE_STOPPED:
+ if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[0].enmState, RTTIMERLNXSTATE_STARTING, RTTIMERLNXSTATE_STOPPED))
+ {
+ ASMAtomicWriteBool(&pTimer->fSuspended, false);
+ if (!pTimer->fSpecificCpu)
+ rtTimerLnxStartSubTimer(&pTimer->aSubTimers[0], Args.u64Now, Args.u64First,
+ false /*fPinned*/, pTimer->fHighRes);
+ else
+ {
+ rc2 = RTMpOnSpecific(pTimer->idCpu, rtTimerLnxStartOnSpecificCpu, pTimer, &Args);
+ if (RT_FAILURE(rc2))
+ {
+ /* Suspend it, the cpu id is probably invalid or offline. */
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+ rtTimerLnxSetState(&pTimer->aSubTimers[0].enmState, RTTIMERLNXSTATE_STOPPED);
+ return rc2;
+ }
+ }
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ break;
+
+ case RTTIMERLNXSTATE_CALLBACK:
+ case RTTIMERLNXSTATE_CB_STOPPING:
+ if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[0].enmState, RTTIMERLNXSTATE_CB_RESTARTING, enmState))
+ {
+ ASMAtomicWriteBool(&pTimer->fSuspended, false);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+ break;
+
+ default:
+ AssertMsgFailed(("%d\n", enmState));
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INTERNAL_ERROR_4;
+ }
+ ASMNopPause();
+ }
+}
+RT_EXPORT_SYMBOL(RTTimerStart);
+
+
+/**
+ * Common worker for RTTimerStop and RTTimerDestroy.
+ *
+ * @returns true if there was any active callbacks, false if not.
+ * @param pTimer The timer to stop.
+ * @param fForDestroy Whether it's RTTimerDestroy calling or not.
+ */
+static bool rtTimerLnxStop(PRTTIMER pTimer, bool fForDestroy)
+{
+ RTTIMERLNX_LOG(("lnxstop %p %d\n", pTimer, fForDestroy));
+#ifdef CONFIG_SMP
+ /*
+ * Omni timer?
+ */
+ if (pTimer->fAllCpus)
+ return rtTimerLnxOmniStop(pTimer, fForDestroy);
+#endif
+
+ /*
+ * Simple timer.
+ */
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+ for (;;)
+ {
+ RTTIMERLNXSTATE enmState = rtTimerLnxGetState(&pTimer->aSubTimers[0].enmState);
+ switch (enmState)
+ {
+ case RTTIMERLNXSTATE_ACTIVE:
+ if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[0].enmState, RTTIMERLNXSTATE_STOPPING, RTTIMERLNXSTATE_ACTIVE))
+ {
+ rtTimerLnxStopSubTimer(&pTimer->aSubTimers[0], pTimer->fHighRes);
+ return false;
+ }
+ break;
+
+ case RTTIMERLNXSTATE_CALLBACK:
+ case RTTIMERLNXSTATE_CB_RESTARTING:
+ case RTTIMERLNXSTATE_CB_STOPPING:
+ Assert(enmState != RTTIMERLNXSTATE_CB_STOPPING || fForDestroy);
+ if (rtTimerLnxCmpXchgState(&pTimer->aSubTimers[0].enmState,
+ !fForDestroy ? RTTIMERLNXSTATE_CB_STOPPING : RTTIMERLNXSTATE_CB_DESTROYING,
+ enmState))
+ return true;
+ break;
+
+ case RTTIMERLNXSTATE_STOPPED:
+ return VINF_SUCCESS;
+
+ case RTTIMERLNXSTATE_CB_DESTROYING:
+ AssertMsgFailed(("enmState=%d pTimer=%p\n", enmState, pTimer));
+ return true;
+
+ default:
+ case RTTIMERLNXSTATE_STARTING:
+ case RTTIMERLNXSTATE_MP_STARTING:
+ case RTTIMERLNXSTATE_STOPPING:
+ case RTTIMERLNXSTATE_MP_STOPPING:
+ AssertMsgFailed(("enmState=%d pTimer=%p\n", enmState, pTimer));
+ return false;
+ }
+
+ /* State not stable, try again. */
+ ASMNopPause();
+ }
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ /*
+ * Validate.
+ */
+ IPRT_LINUX_SAVE_EFL_AC();
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+ RTTIMERLNX_LOG(("stop %p\n", pTimer));
+
+ if (ASMAtomicUoReadBool(&pTimer->fSuspended))
+ return VERR_TIMER_SUSPENDED;
+
+ rtTimerLnxStop(pTimer, false /*fForDestroy*/);
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTTimerStop);
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ unsigned long cJiffies;
+ unsigned long flFlags;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(u64NanoInterval, VERR_INVALID_PARAMETER);
+ AssertReturn(u64NanoInterval < UINT64_MAX / 8, VERR_INVALID_PARAMETER);
+ AssertReturn(pTimer->u64NanoInterval, VERR_INVALID_STATE);
+ RTTIMERLNX_LOG(("change %p %llu\n", pTimer, u64NanoInterval));
+
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+ /*
+ * For the high resolution timers it is easy since we don't care so much
+ * about when it is applied to the sub-timers.
+ */
+ if (pTimer->fHighRes)
+ {
+ ASMAtomicWriteU64(&pTimer->u64NanoInterval, u64NanoInterval);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+ }
+#endif
+
+ /*
+ * Standard timers have a bit more complicated way of calculating
+ * their interval and such. So, forget omni timers for now.
+ */
+ if (pTimer->cCpus > 1)
+ return VERR_NOT_SUPPORTED;
+
+ cJiffies = u64NanoInterval / (RT_NS_1SEC / HZ);
+ if (cJiffies * (RT_NS_1SEC / HZ) != u64NanoInterval)
+ cJiffies = 0;
+
+ spin_lock_irqsave(&pTimer->ChgIntLock, flFlags);
+ pTimer->aSubTimers[0].u.Std.fFirstAfterChg = true;
+ pTimer->cJiffies = cJiffies;
+ ASMAtomicWriteU64(&pTimer->u64NanoInterval, u64NanoInterval);
+ spin_unlock_irqrestore(&pTimer->ChgIntLock, flFlags);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTTimerChangeInterval);
+
+
+RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ bool fCanDestroy;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ /*
+ * Validate. It's ok to pass NULL pointer.
+ */
+ if (pTimer == /*NIL_RTTIMER*/ NULL)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+ RTTIMERLNX_LOG(("destroy %p\n", pTimer));
+/** @todo We should invalidate the magic here! */
+
+ /*
+ * Stop the timer if it's still active, then destroy it if we can.
+ */
+ if (!ASMAtomicUoReadBool(&pTimer->fSuspended))
+ fCanDestroy = rtTimerLnxStop(pTimer, true /*fForDestroy*/);
+ else
+ {
+ uint32_t iCpu = pTimer->cCpus;
+ if (pTimer->cCpus > 1)
+ RTSpinlockAcquire(pTimer->hSpinlock);
+
+ fCanDestroy = true;
+ while (iCpu-- > 0)
+ {
+ for (;;)
+ {
+ RTTIMERLNXSTATE enmState = rtTimerLnxGetState(&pTimer->aSubTimers[iCpu].enmState);
+ switch (enmState)
+ {
+ case RTTIMERLNXSTATE_CALLBACK:
+ case RTTIMERLNXSTATE_CB_RESTARTING:
+ case RTTIMERLNXSTATE_CB_STOPPING:
+ if (!rtTimerLnxCmpXchgState(&pTimer->aSubTimers[iCpu].enmState, RTTIMERLNXSTATE_CB_DESTROYING, enmState))
+ continue;
+ fCanDestroy = false;
+ break;
+
+ case RTTIMERLNXSTATE_CB_DESTROYING:
+ AssertMsgFailed(("%d\n", enmState));
+ fCanDestroy = false;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ if (pTimer->cCpus > 1)
+ RTSpinlockRelease(pTimer->hSpinlock);
+ }
+
+ if (fCanDestroy)
+ {
+ /* For paranoid reasons, defer actually destroying the semaphore when
+ in atomic or interrupt context. */
+#if RTLNX_VER_MIN(2,5,32)
+ if (in_atomic() || in_interrupt())
+#else
+ if (in_interrupt())
+#endif
+ rtR0LnxWorkqueuePush(&pTimer->DtorWorkqueueItem, rtTimerLnxDestroyDeferred);
+ else
+ rtTimerLnxDestroyIt(pTimer);
+ }
+
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTTimerDestroy);
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ PRTTIMER pTimer;
+ RTCPUID iCpu;
+ unsigned cCpus;
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC();
+
+ rtR0LnxWorkqueueFlush(); /* for 2.4 */
+ *ppTimer = NULL;
+
+ /*
+ * Validate flags.
+ */
+ if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
+ && !RTMpIsCpuPossible(RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK)))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VERR_CPU_NOT_FOUND;
+ }
+
+ /*
+ * Allocate the timer handler.
+ */
+ cCpus = 1;
+#ifdef CONFIG_SMP
+ if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL)
+ {
+ cCpus = RTMpGetMaxCpuId() + 1;
+ Assert(cCpus <= RTCPUSET_MAX_CPUS); /* On linux we have a 1:1 relationship between cpuid and set index. */
+ AssertReturnStmt(u64NanoInterval, IPRT_LINUX_RESTORE_EFL_AC(), VERR_NOT_IMPLEMENTED); /* We don't implement single shot on all cpus, sorry. */
+ }
+#endif
+
+ rc = RTMemAllocEx(RT_UOFFSETOF_DYN(RTTIMER, aSubTimers[cCpus]), 0,
+ RTMEMALLOCEX_FLAGS_ZEROED | RTMEMALLOCEX_FLAGS_ANY_CTX_FREE, (void **)&pTimer);
+ if (RT_FAILURE(rc))
+ {
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+ }
+
+ /*
+ * Initialize it.
+ */
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->hSpinlock = NIL_RTSPINLOCK;
+ pTimer->fSuspended = true;
+ pTimer->fHighRes = !!(fFlags & RTTIMER_FLAGS_HIGH_RES);
+#ifdef CONFIG_SMP
+ pTimer->fSpecificCpu = (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL;
+ pTimer->fAllCpus = (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL;
+ pTimer->idCpu = pTimer->fSpecificCpu
+ ? RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK)
+ : NIL_RTCPUID;
+#else
+ pTimer->fSpecificCpu = !!(fFlags & RTTIMER_FLAGS_CPU_SPECIFIC);
+ pTimer->idCpu = RTMpCpuId();
+#endif
+ pTimer->cCpus = cCpus;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->u64NanoInterval = u64NanoInterval;
+ pTimer->cJiffies = u64NanoInterval / (RT_NS_1SEC / HZ);
+ if (pTimer->cJiffies * (RT_NS_1SEC / HZ) != u64NanoInterval)
+ pTimer->cJiffies = 0;
+ spin_lock_init(&pTimer->ChgIntLock);
+
+ for (iCpu = 0; iCpu < cCpus; iCpu++)
+ {
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+ if (pTimer->fHighRes)
+ {
+ hrtimer_init(&pTimer->aSubTimers[iCpu].u.Hr.LnxTimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ pTimer->aSubTimers[iCpu].u.Hr.LnxTimer.function = rtTimerLinuxHrCallback;
+ }
+ else
+#endif
+ {
+#if RTLNX_VER_MIN(4,15,0)
+ timer_setup(&pTimer->aSubTimers[iCpu].u.Std.LnxTimer, rtTimerLinuxStdCallback, TIMER_PINNED);
+#elif RTLNX_VER_MIN(4,8,0)
+ init_timer_pinned(&pTimer->aSubTimers[iCpu].u.Std.LnxTimer);
+#else
+ init_timer(&pTimer->aSubTimers[iCpu].u.Std.LnxTimer);
+#endif
+#if RTLNX_VER_MAX(4,15,0)
+ pTimer->aSubTimers[iCpu].u.Std.LnxTimer.data = (unsigned long)&pTimer->aSubTimers[iCpu];
+ pTimer->aSubTimers[iCpu].u.Std.LnxTimer.function = rtTimerLinuxStdCallback;
+#endif
+ pTimer->aSubTimers[iCpu].u.Std.LnxTimer.expires = jiffies;
+ pTimer->aSubTimers[iCpu].u.Std.u64NextTS = 0;
+ }
+ pTimer->aSubTimers[iCpu].iTick = 0;
+ pTimer->aSubTimers[iCpu].pParent = pTimer;
+ pTimer->aSubTimers[iCpu].enmState = RTTIMERLNXSTATE_STOPPED;
+ }
+
+#ifdef CONFIG_SMP
+ /*
+ * If this is running on ALL cpus, we'll have to register a callback
+ * for MP events (so timers can be started/stopped on cpus going
+ * online/offline). We also create the spinlock for synchronizing
+ * stop/start/mp-event.
+ */
+ if (cCpus > 1)
+ {
+ int rc = RTSpinlockCreate(&pTimer->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTTimerLnx");
+ if (RT_SUCCESS(rc))
+ rc = RTMpNotificationRegister(rtTimerLinuxMpEvent, pTimer);
+ else
+ pTimer->hSpinlock = NIL_RTSPINLOCK;
+ if (RT_FAILURE(rc))
+ {
+ RTTimerDestroy(pTimer);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return rc;
+ }
+ }
+#endif /* CONFIG_SMP */
+
+ RTTIMERLNX_LOG(("create %p hires=%d fFlags=%#x cCpus=%u\n", pTimer, pTimer->fHighRes, fFlags, cCpus));
+ *ppTimer = pTimer;
+ IPRT_LINUX_RESTORE_EFL_AC();
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTTimerCreateEx);
+
+
+RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
+{
+#if 0 /** @todo Not sure if this is what we want or not... Add new API for
+ * querying the resolution of the high res timers? */
+ struct timespec Ts;
+ int rc;
+ IPRT_LINUX_SAVE_EFL_AC();
+ rc = hrtimer_get_res(CLOCK_MONOTONIC, &Ts);
+ IPRT_LINUX_RESTORE_EFL_AC();
+ if (!rc)
+ {
+ Assert(!Ts.tv_sec);
+ return Ts.tv_nsec;
+ }
+#endif
+ /* */
+#if RTLNX_VER_MAX(4,9,0) || RTLNX_VER_MIN(4,13,0)
+ /* On 4.9, 4.10 and 4.12 we've observed tstRTR0Timer failures of the omni timer tests
+ where we get about half of the ticks we want. The failing test is using this value
+ as interval. So, this is a very very crude hack to try make omni timers work
+ correctly without actually knowing what's going wrong... */
+ return RT_NS_1SEC * 2 / HZ; /* ns */
+#else
+ return RT_NS_1SEC / HZ; /* ns */
+#endif
+}
+RT_EXPORT_SYMBOL(RTTimerGetSystemGranularity);
+
+
+RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
+{
+ RT_NOREF_PV(u32Request); RT_NOREF_PV(*pu32Granted);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTTimerRequestSystemGranularity);
+
+
+RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
+{
+ RT_NOREF_PV(u32Granted);
+ return VERR_NOT_SUPPORTED;
+}
+RT_EXPORT_SYMBOL(RTTimerReleaseSystemGranularity);
+
+
+RTDECL(bool) RTTimerCanDoHighResolution(void)
+{
+#ifdef RTTIMER_LINUX_WITH_HRTIMER
+ return true;
+#else
+ return false;
+#endif
+}
+RT_EXPORT_SYMBOL(RTTimerCanDoHighResolution);
+
diff --git a/src/VBox/Runtime/r0drv/linux/waitqueue-r0drv-linux.h b/src/VBox/Runtime/r0drv/linux/waitqueue-r0drv-linux.h
new file mode 100644
index 00000000..0ef103fe
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/linux/waitqueue-r0drv-linux.h
@@ -0,0 +1,302 @@
+/* $Id: waitqueue-r0drv-linux.h $ */
+/** @file
+ * IPRT - Linux Ring-0 Driver Helpers for Abstracting Wait Queues,
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_linux_waitqueue_r0drv_linux_h
+#define IPRT_INCLUDED_SRC_r0drv_linux_waitqueue_r0drv_linux_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "the-linux-kernel.h"
+
+#include <iprt/asm-math.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+/** The resolution (nanoseconds) specified when using
+ * schedule_hrtimeout_range. */
+#define RTR0SEMLNXWAIT_RESOLUTION 50000
+
+
+/**
+ * Kernel mode Linux wait state structure.
+ */
+typedef struct RTR0SEMLNXWAIT
+{
+ /** The wait queue entry. */
+#if RTLNX_VER_MIN(4,13,0) || RTLNX_SUSE_MAJ_PREREQ(12, 4) || RTLNX_SUSE_MAJ_PREREQ(15, 0)
+ wait_queue_entry_t WaitQE;
+#else
+ wait_queue_t WaitQE;
+#endif
+ /** The absolute timeout given as nano seconds since the start of the
+ * monotonic clock. */
+ uint64_t uNsAbsTimeout;
+ /** The timeout in nano seconds relative to the start of the wait. */
+ uint64_t cNsRelTimeout;
+ /** The native timeout value. */
+ union
+ {
+#ifdef IPRT_LINUX_HAS_HRTIMER
+ /** The timeout when fHighRes is true. Absolute, so no updating. */
+ ktime_t KtTimeout;
+#endif
+ /** The timeout when fHighRes is false. Updated after waiting. */
+ long lTimeout;
+ } u;
+ /** Set if we use high resolution timeouts. */
+ bool fHighRes;
+ /** Set if it's an indefinite wait. */
+ bool fIndefinite;
+ /** Set if we've already timed out.
+ * Set by rtR0SemLnxWaitDoIt and read by rtR0SemLnxWaitHasTimedOut. */
+ bool fTimedOut;
+ /** TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE. */
+ int iWaitState;
+ /** The wait queue. */
+ wait_queue_head_t *pWaitQueue;
+} RTR0SEMLNXWAIT;
+/** Pointer to a linux wait state. */
+typedef RTR0SEMLNXWAIT *PRTR0SEMLNXWAIT;
+
+
+/**
+ * Initializes a wait.
+ *
+ * The caller MUST check the wait condition BEFORE calling this function or the
+ * timeout logic will be flawed.
+ *
+ * @returns VINF_SUCCESS or VERR_TIMEOUT.
+ * @param pWait The wait structure.
+ * @param fFlags The wait flags.
+ * @param uTimeout The timeout.
+ * @param pWaitQueue The wait queue head.
+ */
+DECLINLINE(int) rtR0SemLnxWaitInit(PRTR0SEMLNXWAIT pWait, uint32_t fFlags, uint64_t uTimeout,
+ wait_queue_head_t *pWaitQueue)
+{
+ /*
+ * Process the flags and timeout.
+ */
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+/** @todo optimize: millisecs -> nanosecs -> millisec -> jiffies */
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ uTimeout = uTimeout < UINT64_MAX / RT_US_1SEC * RT_US_1SEC
+ ? uTimeout * RT_US_1SEC
+ : UINT64_MAX;
+ if (uTimeout == UINT64_MAX)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ {
+ uint64_t u64Now;
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ {
+ if (uTimeout == 0)
+ return VERR_TIMEOUT;
+
+ u64Now = RTTimeSystemNanoTS();
+ pWait->cNsRelTimeout = uTimeout;
+ pWait->uNsAbsTimeout = u64Now + uTimeout;
+ if (pWait->uNsAbsTimeout < u64Now) /* overflow */
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ }
+ else
+ {
+ u64Now = RTTimeSystemNanoTS();
+ if (u64Now >= uTimeout)
+ return VERR_TIMEOUT;
+
+ pWait->cNsRelTimeout = uTimeout - u64Now;
+ pWait->uNsAbsTimeout = uTimeout;
+ }
+ }
+ }
+
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+ pWait->fIndefinite = false;
+#ifdef IPRT_LINUX_HAS_HRTIMER
+ if ( (fFlags & (RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_ABSOLUTE))
+ || pWait->cNsRelTimeout < RT_NS_1SEC / HZ * 4)
+ {
+ pWait->fHighRes = true;
+# if BITS_PER_LONG < 64
+ if ( KTIME_SEC_MAX <= LONG_MAX
+ && pWait->uNsAbsTimeout >= KTIME_SEC_MAX * RT_NS_1SEC_64 + (RT_NS_1SEC - 1))
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+# endif
+ pWait->u.KtTimeout = ns_to_ktime(pWait->uNsAbsTimeout);
+ }
+ else
+#endif
+ {
+ uint64_t cJiffies = ASMMultU64ByU32DivByU32(pWait->cNsRelTimeout, HZ, RT_NS_1SEC);
+ if (cJiffies >= MAX_JIFFY_OFFSET)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ {
+ pWait->u.lTimeout = (long)cJiffies;
+ pWait->fHighRes = false;
+ }
+ }
+ }
+
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ {
+ pWait->fIndefinite = true;
+ pWait->fHighRes = false;
+ pWait->uNsAbsTimeout = UINT64_MAX;
+ pWait->cNsRelTimeout = UINT64_MAX;
+ pWait->u.lTimeout = LONG_MAX;
+ }
+
+ pWait->fTimedOut = false;
+
+ /*
+ * Initialize the wait queue related bits.
+ */
+#if RTLNX_VER_MIN(2,5,39)
+ init_wait((&pWait->WaitQE));
+#else
+ RT_ZERO(pWait->WaitQE);
+ init_waitqueue_entry((&pWait->WaitQE), current);
+#endif
+ pWait->pWaitQueue = pWaitQueue;
+ pWait->iWaitState = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE
+ ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Prepares the next wait.
+ *
+ * This must be called before rtR0SemLnxWaitDoIt, and the caller should check
+ * the exit conditions in-between the two calls.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemLnxWaitPrepare(PRTR0SEMLNXWAIT pWait)
+{
+ /* Make everything thru schedule*() atomic scheduling wise. (Is this correct?) */
+ prepare_to_wait(pWait->pWaitQueue, &pWait->WaitQE, pWait->iWaitState);
+}
+
+
+/**
+ * Do the actual wait.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemLnxWaitDoIt(PRTR0SEMLNXWAIT pWait)
+{
+ if (pWait->fIndefinite)
+ schedule();
+#ifdef IPRT_LINUX_HAS_HRTIMER
+ else if (pWait->fHighRes)
+ {
+ int rc = schedule_hrtimeout_range(&pWait->u.KtTimeout, HRTIMER_MODE_ABS, RTR0SEMLNXWAIT_RESOLUTION);
+ if (!rc)
+ pWait->fTimedOut = true;
+ }
+#endif
+ else
+ {
+ pWait->u.lTimeout = schedule_timeout(pWait->u.lTimeout);
+ if (pWait->u.lTimeout <= 0)
+ pWait->fTimedOut = true;
+ }
+ after_wait((&pWait->WaitQE));
+}
+
+
+/**
+ * Checks if a linux wait was interrupted.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ * @remarks This shall be called before the first rtR0SemLnxWaitDoIt().
+ */
+DECLINLINE(bool) rtR0SemLnxWaitWasInterrupted(PRTR0SEMLNXWAIT pWait)
+{
+ return pWait->iWaitState == TASK_INTERRUPTIBLE
+ && signal_pending(current);
+}
+
+
+/**
+ * Checks if a linux wait has timed out.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ */
+DECLINLINE(bool) rtR0SemLnxWaitHasTimedOut(PRTR0SEMLNXWAIT pWait)
+{
+ return pWait->fTimedOut;
+}
+
+
+/**
+ * Deletes a linux wait.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemLnxWaitDelete(PRTR0SEMLNXWAIT pWait)
+{
+ finish_wait(pWait->pWaitQueue, &pWait->WaitQE);
+}
+
+
+/**
+ * Gets the max resolution of the timeout machinery.
+ *
+ * @returns Resolution specified in nanoseconds.
+ */
+DECLINLINE(uint32_t) rtR0SemLnxWaitGetResolution(void)
+{
+#ifdef IPRT_LINUX_HAS_HRTIMER
+ return RTR0SEMLNXWAIT_RESOLUTION;
+#else
+ return RT_NS_1SEC / HZ; /* ns */
+#endif
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_linux_waitqueue_r0drv_linux_h */
+
diff --git a/src/VBox/Runtime/r0drv/memobj-r0drv.cpp b/src/VBox/Runtime/r0drv/memobj-r0drv.cpp
new file mode 100644
index 00000000..222c3c5a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/memobj-r0drv.cpp
@@ -0,0 +1,824 @@
+/* $Id: memobj-r0drv.cpp $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, 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 RTLOGGROUP_DEFAULT /// @todo RTLOGGROUP_MEM
+#define RTMEM_NO_WRAP_TO_EF_APIS /* circular dependency otherwise. */
+#include <iprt/memobj.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloc.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mp.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+
+#include "internal/memobj.h"
+
+
+/**
+ * Internal function for allocating a new memory object.
+ *
+ * @returns The allocated and initialized handle.
+ * @param cbSelf The size of the memory object handle. 0 mean default size.
+ * @param enmType The memory object type.
+ * @param pv The memory object mapping.
+ * @param cb The size of the memory object.
+ * @param pszTag The tag string.
+ */
+DECLHIDDEN(PRTR0MEMOBJINTERNAL) rtR0MemObjNew(size_t cbSelf, RTR0MEMOBJTYPE enmType, void *pv, size_t cb, const char *pszTag)
+{
+ PRTR0MEMOBJINTERNAL pNew;
+
+ /* validate the size */
+ if (!cbSelf)
+ cbSelf = sizeof(*pNew);
+ Assert(cbSelf >= sizeof(*pNew));
+ Assert(cbSelf == (uint32_t)cbSelf);
+ AssertMsg(RT_ALIGN_Z(cb, PAGE_SIZE) == cb, ("%#zx\n", cb));
+
+ /*
+ * Allocate and initialize the object.
+ */
+ pNew = (PRTR0MEMOBJINTERNAL)RTMemAllocZ(cbSelf);
+ if (pNew)
+ {
+ pNew->u32Magic = RTR0MEMOBJ_MAGIC;
+ pNew->cbSelf = (uint32_t)cbSelf;
+ pNew->enmType = enmType;
+ pNew->fFlags = 0;
+ pNew->cb = cb;
+ pNew->pv = pv;
+#ifdef DEBUG
+ pNew->pszTag = pszTag;
+#else
+ RT_NOREF_PV(pszTag);
+#endif
+ }
+ return pNew;
+}
+
+
+/**
+ * Deletes an incomplete memory object.
+ *
+ * This is for cleaning up after failures during object creation.
+ *
+ * @param pMem The incomplete memory object to delete.
+ */
+DECLHIDDEN(void) rtR0MemObjDelete(PRTR0MEMOBJINTERNAL pMem)
+{
+ if (pMem)
+ {
+ ASMAtomicUoWriteU32(&pMem->u32Magic, ~RTR0MEMOBJ_MAGIC);
+ pMem->enmType = RTR0MEMOBJTYPE_END;
+ RTMemFree(pMem);
+ }
+}
+
+
+/**
+ * Links a mapping object to a primary object.
+ *
+ * @returns IPRT status code.
+ * @retval VINF_SUCCESS on success.
+ * @retval VINF_NO_MEMORY if we couldn't expand the mapping array of the parent.
+ * @param pParent The parent (primary) memory object.
+ * @param pChild The child (mapping) memory object.
+ */
+static int rtR0MemObjLink(PRTR0MEMOBJINTERNAL pParent, PRTR0MEMOBJINTERNAL pChild)
+{
+ uint32_t i;
+
+ /* sanity */
+ Assert(rtR0MemObjIsMapping(pChild));
+ Assert(!rtR0MemObjIsMapping(pParent));
+
+ /* expand the array? */
+ i = pParent->uRel.Parent.cMappings;
+ if (i >= pParent->uRel.Parent.cMappingsAllocated)
+ {
+ void *pv = RTMemRealloc(pParent->uRel.Parent.papMappings,
+ (i + 32) * sizeof(pParent->uRel.Parent.papMappings[0]));
+ if (!pv)
+ return VERR_NO_MEMORY;
+ pParent->uRel.Parent.papMappings = (PPRTR0MEMOBJINTERNAL)pv;
+ pParent->uRel.Parent.cMappingsAllocated = i + 32;
+ Assert(i == pParent->uRel.Parent.cMappings);
+ }
+
+ /* do the linking. */
+ pParent->uRel.Parent.papMappings[i] = pChild;
+ pParent->uRel.Parent.cMappings++;
+ pChild->uRel.Child.pParent = pParent;
+
+ return VINF_SUCCESS;
+}
+
+
+RTR0DECL(bool) RTR0MemObjIsMapping(RTR0MEMOBJ MemObj)
+{
+ /* Validate the object handle. */
+ PRTR0MEMOBJINTERNAL pMem;
+ AssertPtrReturn(MemObj, false);
+ pMem = (PRTR0MEMOBJINTERNAL)MemObj;
+ AssertMsgReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, ("%p: %#x\n", pMem, pMem->u32Magic), false);
+ AssertMsgReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, ("%p: %d\n", pMem, pMem->enmType), false);
+
+ /* hand it on to the inlined worker. */
+ return rtR0MemObjIsMapping(pMem);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjIsMapping);
+
+
+RTR0DECL(void *) RTR0MemObjAddress(RTR0MEMOBJ MemObj)
+{
+ /* Validate the object handle. */
+ PRTR0MEMOBJINTERNAL pMem;
+ if (RT_UNLIKELY(MemObj == NIL_RTR0MEMOBJ))
+ return NULL;
+ AssertPtrReturn(MemObj, NULL);
+ pMem = (PRTR0MEMOBJINTERNAL)MemObj;
+ AssertMsgReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, ("%p: %#x\n", pMem, pMem->u32Magic), NULL);
+ AssertMsgReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, ("%p: %d\n", pMem, pMem->enmType), NULL);
+
+ /* return the mapping address. */
+ return pMem->pv;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAddress);
+
+
+RTR0DECL(RTR3PTR) RTR0MemObjAddressR3(RTR0MEMOBJ MemObj)
+{
+ PRTR0MEMOBJINTERNAL pMem;
+
+ /* Validate the object handle. */
+ if (RT_UNLIKELY(MemObj == NIL_RTR0MEMOBJ))
+ return NIL_RTR3PTR;
+ AssertPtrReturn(MemObj, NIL_RTR3PTR);
+ pMem = (PRTR0MEMOBJINTERNAL)MemObj;
+ AssertMsgReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, ("%p: %#x\n", pMem, pMem->u32Magic), NIL_RTR3PTR);
+ AssertMsgReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, ("%p: %d\n", pMem, pMem->enmType), NIL_RTR3PTR);
+ if (RT_UNLIKELY( ( pMem->enmType != RTR0MEMOBJTYPE_MAPPING
+ || pMem->u.Mapping.R0Process == NIL_RTR0PROCESS)
+ && ( pMem->enmType != RTR0MEMOBJTYPE_LOCK
+ || pMem->u.Lock.R0Process == NIL_RTR0PROCESS)
+ && ( pMem->enmType != RTR0MEMOBJTYPE_PHYS_NC
+ || pMem->u.Lock.R0Process == NIL_RTR0PROCESS)
+ && ( pMem->enmType != RTR0MEMOBJTYPE_RES_VIRT
+ || pMem->u.ResVirt.R0Process == NIL_RTR0PROCESS)))
+ return NIL_RTR3PTR;
+
+ /* return the mapping address. */
+ return (RTR3PTR)pMem->pv;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAddressR3);
+
+
+RTR0DECL(size_t) RTR0MemObjSize(RTR0MEMOBJ MemObj)
+{
+ PRTR0MEMOBJINTERNAL pMem;
+
+ /* Validate the object handle. */
+ if (RT_UNLIKELY(MemObj == NIL_RTR0MEMOBJ))
+ return 0;
+ AssertPtrReturn(MemObj, 0);
+ pMem = (PRTR0MEMOBJINTERNAL)MemObj;
+ AssertMsgReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, ("%p: %#x\n", pMem, pMem->u32Magic), 0);
+ AssertMsgReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, ("%p: %d\n", pMem, pMem->enmType), 0);
+ AssertMsg(RT_ALIGN_Z(pMem->cb, PAGE_SIZE) == pMem->cb, ("%#zx\n", pMem->cb));
+
+ /* return the size. */
+ return pMem->cb;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjSize);
+
+
+/* Work around gcc bug 55940 */
+#if defined(__GNUC__) && defined(RT_ARCH_X86) && (__GNUC__ * 100 + __GNUC_MINOR__) == 407
+ __attribute__((__optimize__ ("no-shrink-wrap")))
+#endif
+RTR0DECL(RTHCPHYS) RTR0MemObjGetPagePhysAddr(RTR0MEMOBJ MemObj, size_t iPage)
+{
+ /* Validate the object handle. */
+ PRTR0MEMOBJINTERNAL pMem;
+ size_t cPages;
+ AssertPtrReturn(MemObj, NIL_RTHCPHYS);
+ pMem = (PRTR0MEMOBJINTERNAL)MemObj;
+ AssertReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, NIL_RTHCPHYS);
+ AssertReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, NIL_RTHCPHYS);
+ AssertMsgReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, ("%p: %#x\n", pMem, pMem->u32Magic), NIL_RTHCPHYS);
+ AssertMsgReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, ("%p: %d\n", pMem, pMem->enmType), NIL_RTHCPHYS);
+ cPages = (pMem->cb >> PAGE_SHIFT);
+ if (iPage >= cPages)
+ {
+ /* permit: while (RTR0MemObjGetPagePhysAddr(pMem, iPage++) != NIL_RTHCPHYS) {} */
+ if (iPage == cPages)
+ return NIL_RTHCPHYS;
+ AssertReturn(iPage < (pMem->cb >> PAGE_SHIFT), NIL_RTHCPHYS);
+ }
+
+ /*
+ * We know the address of physically contiguous allocations and mappings.
+ */
+ if (pMem->enmType == RTR0MEMOBJTYPE_CONT)
+ return pMem->u.Cont.Phys + iPage * PAGE_SIZE;
+ if (pMem->enmType == RTR0MEMOBJTYPE_PHYS)
+ return pMem->u.Phys.PhysBase + iPage * PAGE_SIZE;
+
+ /*
+ * Do the job.
+ */
+ return rtR0MemObjNativeGetPagePhysAddr(pMem, iPage);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjGetPagePhysAddr);
+
+
+RTR0DECL(bool) RTR0MemObjWasZeroInitialized(RTR0MEMOBJ hMemObj)
+{
+ PRTR0MEMOBJINTERNAL pMem;
+
+ /* Validate the object handle. */
+ if (RT_UNLIKELY(hMemObj == NIL_RTR0MEMOBJ))
+ return false;
+ AssertPtrReturn(hMemObj, false);
+ pMem = (PRTR0MEMOBJINTERNAL)hMemObj;
+ AssertMsgReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, ("%p: %#x\n", pMem, pMem->u32Magic), false);
+ AssertMsgReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, ("%p: %d\n", pMem, pMem->enmType), false);
+ Assert( (pMem->fFlags & (RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC | RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC))
+ != (RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC | RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC));
+
+ /* return the alloc init state. */
+ return (pMem->fFlags & (RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC | RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC))
+ == RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjWasZeroInitialized);
+
+
+RTR0DECL(int) RTR0MemObjFree(RTR0MEMOBJ MemObj, bool fFreeMappings)
+{
+ /*
+ * Validate the object handle.
+ */
+ PRTR0MEMOBJINTERNAL pMem;
+ int rc;
+
+ if (MemObj == NIL_RTR0MEMOBJ)
+ return VINF_SUCCESS;
+ AssertPtrReturn(MemObj, VERR_INVALID_HANDLE);
+ pMem = (PRTR0MEMOBJINTERNAL)MemObj;
+ AssertReturn(pMem->u32Magic == RTR0MEMOBJ_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pMem->enmType > RTR0MEMOBJTYPE_INVALID && pMem->enmType < RTR0MEMOBJTYPE_END, VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /*
+ * Deal with mappings according to fFreeMappings.
+ */
+ if ( !rtR0MemObjIsMapping(pMem)
+ && pMem->uRel.Parent.cMappings > 0)
+ {
+ /* fail if not requested to free mappings. */
+ if (!fFreeMappings)
+ return VERR_MEMORY_BUSY;
+
+ while (pMem->uRel.Parent.cMappings > 0)
+ {
+ PRTR0MEMOBJINTERNAL pChild = pMem->uRel.Parent.papMappings[--pMem->uRel.Parent.cMappings];
+ pMem->uRel.Parent.papMappings[pMem->uRel.Parent.cMappings] = NULL;
+
+ /* sanity checks. */
+ AssertPtr(pChild);
+ AssertFatal(pChild->u32Magic == RTR0MEMOBJ_MAGIC);
+ AssertFatal(pChild->enmType > RTR0MEMOBJTYPE_INVALID && pChild->enmType < RTR0MEMOBJTYPE_END);
+ AssertFatal(rtR0MemObjIsMapping(pChild));
+
+ /* free the mapping. */
+ rc = rtR0MemObjNativeFree(pChild);
+ if (RT_FAILURE(rc))
+ {
+ Log(("RTR0MemObjFree: failed to free mapping %p: %p %#zx; rc=%Rrc\n", pChild, pChild->pv, pChild->cb, rc));
+ pMem->uRel.Parent.papMappings[pMem->uRel.Parent.cMappings++] = pChild;
+ return rc;
+ }
+
+ pChild->u32Magic++;
+ pChild->enmType = RTR0MEMOBJTYPE_END;
+ RTMemFree(pChild);
+ }
+ }
+
+ /*
+ * Free this object.
+ */
+ rc = rtR0MemObjNativeFree(pMem);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Ok, it was freed just fine. Now, if it's a mapping we'll have to remove it from the parent.
+ */
+ if (rtR0MemObjIsMapping(pMem))
+ {
+ PRTR0MEMOBJINTERNAL pParent = pMem->uRel.Child.pParent;
+ uint32_t i;
+
+ /* sanity checks */
+ AssertPtr(pParent);
+ AssertFatal(pParent->u32Magic == RTR0MEMOBJ_MAGIC);
+ AssertFatal(pParent->enmType > RTR0MEMOBJTYPE_INVALID && pParent->enmType < RTR0MEMOBJTYPE_END);
+ AssertFatal(!rtR0MemObjIsMapping(pParent));
+ AssertFatal(pParent->uRel.Parent.cMappings > 0);
+ AssertPtr(pParent->uRel.Parent.papMappings);
+
+ /* locate and remove from the array of mappings. */
+ i = pParent->uRel.Parent.cMappings;
+ while (i-- > 0)
+ {
+ if (pParent->uRel.Parent.papMappings[i] == pMem)
+ {
+ pParent->uRel.Parent.papMappings[i] = pParent->uRel.Parent.papMappings[--pParent->uRel.Parent.cMappings];
+ break;
+ }
+ }
+ Assert(i != UINT32_MAX);
+ }
+ else
+ Assert(pMem->uRel.Parent.cMappings == 0);
+
+ /*
+ * Finally, destroy the handle.
+ */
+ pMem->u32Magic++;
+ pMem->enmType = RTR0MEMOBJTYPE_END;
+ if (!rtR0MemObjIsMapping(pMem))
+ RTMemFree(pMem->uRel.Parent.papMappings);
+ RTMemFree(pMem);
+ }
+ else
+ Log(("RTR0MemObjFree: failed to free %p: %d %p %#zx; rc=%Rrc\n",
+ pMem, pMem->enmType, pMem->pv, pMem->cb, rc));
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjFree);
+
+
+
+RTR0DECL(int) RTR0MemObjAllocPageTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeAllocPage(pMemObj, cbAligned, fExecutable, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAllocPageTag);
+
+
+RTR0DECL(int) RTR0MemObjAllocLargeTag(PRTR0MEMOBJ pMemObj, size_t cb, size_t cbLargePage, uint32_t fFlags, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, cbLargePage);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+#ifdef RT_ARCH_AMD64
+ AssertReturn(cbLargePage == _2M || cbLargePage == _1G, VERR_OUT_OF_RANGE);
+#elif defined(RT_ARCH_X86)
+ AssertReturn(cbLargePage == _2M || cbLargePage == _4M, VERR_OUT_OF_RANGE);
+#else
+ AssertReturn(RT_IS_POWER_OF_TWO(cbLargePage), VERR_NOT_POWER_OF_TWO);
+ AssertReturn(cbLargePage > PAGE_SIZE, VERR_OUT_OF_RANGE);
+#endif
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTMEMOBJ_ALLOC_LARGE_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeAllocLarge(pMemObj, cbAligned, cbLargePage, fFlags, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAllocLargeTag);
+
+
+/**
+ * Fallback implementation of rtR0MemObjNativeAllocLarge and implements single
+ * page allocation using rtR0MemObjNativeAllocPhys.
+ */
+DECLHIDDEN(int) rtR0MemObjFallbackAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ RT_NOREF(pszTag, fFlags);
+ if (cb == cbLargePage)
+ return rtR0MemObjNativeAllocPhys(ppMem, cb, NIL_RTHCPHYS, cbLargePage, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR0DECL(int) RTR0MemObjAllocLowTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeAllocLow(pMemObj, cbAligned, fExecutable, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAllocLowTag);
+
+
+RTR0DECL(int) RTR0MemObjAllocContTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeAllocCont(pMemObj, cbAligned, fExecutable, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAllocContTag);
+
+
+RTR0DECL(int) RTR0MemObjLockUserTag(PRTR0MEMOBJ pMemObj, RTR3PTR R3Ptr, size_t cb,
+ uint32_t fAccess, RTR0PROCESS R0Process, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb + (R3Ptr & PAGE_OFFSET_MASK), PAGE_SIZE);
+ RTR3PTR const R3PtrAligned = (R3Ptr & ~(RTR3PTR)PAGE_OFFSET_MASK);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ if (R0Process == NIL_RTR0PROCESS)
+ R0Process = RTR0ProcHandleSelf();
+ AssertReturn(!(fAccess & ~(RTMEM_PROT_READ | RTMEM_PROT_WRITE)), VERR_INVALID_PARAMETER);
+ AssertReturn(fAccess, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the locking. */
+ return rtR0MemObjNativeLockUser(pMemObj, R3PtrAligned, cbAligned, fAccess, R0Process, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjLockUserTag);
+
+
+RTR0DECL(int) RTR0MemObjLockKernelTag(PRTR0MEMOBJ pMemObj, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb + ((uintptr_t)pv & PAGE_OFFSET_MASK), PAGE_SIZE);
+ void * const pvAligned = (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pvAligned, VERR_INVALID_POINTER);
+ AssertReturn(!(fAccess & ~(RTMEM_PROT_READ | RTMEM_PROT_WRITE)), VERR_INVALID_PARAMETER);
+ AssertReturn(fAccess, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeLockKernel(pMemObj, pvAligned, cbAligned, fAccess, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjLockKernelTag);
+
+
+RTR0DECL(int) RTR0MemObjAllocPhysTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ AssertReturn(PhysHighest >= cb, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeAllocPhys(pMemObj, cbAligned, PhysHighest, PAGE_SIZE /* page aligned */, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAllocPhysTag);
+
+
+RTR0DECL(int) RTR0MemObjAllocPhysExTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ AssertReturn(PhysHighest >= cb, VERR_INVALID_PARAMETER);
+ if (uAlignment == 0)
+ uAlignment = PAGE_SIZE;
+ AssertReturn( uAlignment == PAGE_SIZE
+ || uAlignment == _2M
+ || uAlignment == _4M
+ || uAlignment == _1G,
+ VERR_INVALID_PARAMETER);
+#if HC_ARCH_BITS == 32
+ /* Memory allocated in this way is typically mapped into kernel space as well; simply
+ don't allow this on 32 bits hosts as the kernel space is too crowded already. */
+ if (uAlignment != PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+#endif
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeAllocPhys(pMemObj, cbAligned, PhysHighest, uAlignment, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAllocPhysExTag);
+
+
+RTR0DECL(int) RTR0MemObjAllocPhysNCTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ AssertReturn(PhysHighest >= cb, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeAllocPhysNC(pMemObj, cbAligned, PhysHighest, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjAllocPhysNCTag);
+
+
+RTR0DECL(int) RTR0MemObjEnterPhysTag(PRTR0MEMOBJ pMemObj, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb + (Phys & PAGE_OFFSET_MASK), PAGE_SIZE);
+ const RTHCPHYS PhysAligned = Phys & ~(RTHCPHYS)PAGE_OFFSET_MASK;
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ AssertReturn(Phys != NIL_RTHCPHYS, VERR_INVALID_PARAMETER);
+ AssertReturn( uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE
+ || uCachePolicy == RTMEM_CACHE_POLICY_MMIO,
+ VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the allocation. */
+ return rtR0MemObjNativeEnterPhys(pMemObj, PhysAligned, cbAligned, uCachePolicy, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjEnterPhysTag);
+
+
+RTR0DECL(int) RTR0MemObjReserveKernelTag(PRTR0MEMOBJ pMemObj, void *pvFixed, size_t cb, size_t uAlignment, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ if (uAlignment == 0)
+ uAlignment = PAGE_SIZE;
+ AssertReturn(uAlignment == PAGE_SIZE || uAlignment == _2M || uAlignment == _4M, VERR_INVALID_PARAMETER);
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ if (pvFixed != (void *)-1)
+ AssertReturn(!((uintptr_t)pvFixed & (uAlignment - 1)), VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the reservation. */
+ return rtR0MemObjNativeReserveKernel(pMemObj, pvFixed, cbAligned, uAlignment, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjReserveKernelTag);
+
+
+RTR0DECL(int) RTR0MemObjReserveUserTag(PRTR0MEMOBJ pMemObj, RTR3PTR R3PtrFixed, size_t cb,
+ size_t uAlignment, RTR0PROCESS R0Process, const char *pszTag)
+{
+ /* sanity checks. */
+ const size_t cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ if (uAlignment == 0)
+ uAlignment = PAGE_SIZE;
+ AssertReturn(uAlignment == PAGE_SIZE || uAlignment == _2M || uAlignment == _4M, VERR_INVALID_PARAMETER);
+ AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cb <= cbAligned, VERR_INVALID_PARAMETER);
+ if (R3PtrFixed != (RTR3PTR)-1)
+ AssertReturn(!(R3PtrFixed & (uAlignment - 1)), VERR_INVALID_PARAMETER);
+ if (R0Process == NIL_RTR0PROCESS)
+ R0Process = RTR0ProcHandleSelf();
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the reservation. */
+ return rtR0MemObjNativeReserveUser(pMemObj, R3PtrFixed, cbAligned, uAlignment, R0Process, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjReserveUserTag);
+
+
+RTR0DECL(int) RTR0MemObjMapKernelTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed,
+ size_t uAlignment, unsigned fProt, const char *pszTag)
+{
+ return RTR0MemObjMapKernelExTag(pMemObj, MemObjToMap, pvFixed, uAlignment, fProt, 0, 0, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjMapKernelTag);
+
+
+RTR0DECL(int) RTR0MemObjMapKernelExTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ PRTR0MEMOBJINTERNAL pMemToMap;
+ PRTR0MEMOBJINTERNAL pNew;
+ int rc;
+
+ /* sanity checks. */
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertPtrReturn(MemObjToMap, VERR_INVALID_HANDLE);
+ pMemToMap = (PRTR0MEMOBJINTERNAL)MemObjToMap;
+ AssertReturn(pMemToMap->u32Magic == RTR0MEMOBJ_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pMemToMap->enmType > RTR0MEMOBJTYPE_INVALID && pMemToMap->enmType < RTR0MEMOBJTYPE_END, VERR_INVALID_HANDLE);
+ AssertReturn(!rtR0MemObjIsMapping(pMemToMap), VERR_INVALID_PARAMETER);
+ AssertReturn(pMemToMap->enmType != RTR0MEMOBJTYPE_RES_VIRT, VERR_INVALID_PARAMETER);
+ if (uAlignment == 0)
+ uAlignment = PAGE_SIZE;
+ AssertReturn(uAlignment == PAGE_SIZE || uAlignment == _2M || uAlignment == _4M, VERR_INVALID_PARAMETER);
+ if (pvFixed != (void *)-1)
+ AssertReturn(!((uintptr_t)pvFixed & (uAlignment - 1)), VERR_INVALID_PARAMETER);
+ AssertReturn(fProt != RTMEM_PROT_NONE, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fProt & ~(RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)), VERR_INVALID_PARAMETER);
+ AssertReturn(!(offSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(offSub < pMemToMap->cb, VERR_INVALID_PARAMETER);
+ AssertReturn(!(cbSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cbSub <= pMemToMap->cb, VERR_INVALID_PARAMETER);
+ AssertReturn((!offSub && !cbSub) || (offSub + cbSub) <= pMemToMap->cb, VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* adjust the request to simplify the native code. */
+ if (offSub == 0 && cbSub == pMemToMap->cb)
+ cbSub = 0;
+
+ /* do the mapping. */
+ rc = rtR0MemObjNativeMapKernel(&pNew, pMemToMap, pvFixed, uAlignment, fProt, offSub, cbSub, pszTag);
+ if (RT_SUCCESS(rc))
+ {
+ /* link it. */
+ rc = rtR0MemObjLink(pMemToMap, pNew);
+ if (RT_SUCCESS(rc))
+ *pMemObj = pNew;
+ else
+ {
+ /* damn, out of memory. bail out. */
+ int rc2 = rtR0MemObjNativeFree(pNew);
+ AssertRC(rc2);
+ pNew->u32Magic++;
+ pNew->enmType = RTR0MEMOBJTYPE_END;
+ RTMemFree(pNew);
+ }
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjMapKernelExTag);
+
+
+RTR0DECL(int) RTR0MemObjMapUserTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, RTR3PTR R3PtrFixed,
+ size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process, const char *pszTag)
+{
+ return RTR0MemObjMapUserExTag(pMemObj, MemObjToMap, R3PtrFixed, uAlignment, fProt, R0Process, 0, 0, pszTag);
+}
+RT_EXPORT_SYMBOL(RTR0MemObjMapUserTag);
+
+
+RTR0DECL(int) RTR0MemObjMapUserExTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ /* sanity checks. */
+ PRTR0MEMOBJINTERNAL pMemToMap;
+ PRTR0MEMOBJINTERNAL pNew;
+ int rc;
+ AssertPtrReturn(pMemObj, VERR_INVALID_POINTER);
+ pMemToMap = (PRTR0MEMOBJINTERNAL)MemObjToMap;
+ *pMemObj = NIL_RTR0MEMOBJ;
+ AssertPtrReturn(MemObjToMap, VERR_INVALID_HANDLE);
+ AssertReturn(pMemToMap->u32Magic == RTR0MEMOBJ_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pMemToMap->enmType > RTR0MEMOBJTYPE_INVALID && pMemToMap->enmType < RTR0MEMOBJTYPE_END, VERR_INVALID_HANDLE);
+ AssertReturn(!rtR0MemObjIsMapping(pMemToMap), VERR_INVALID_PARAMETER);
+ AssertReturn(pMemToMap->enmType != RTR0MEMOBJTYPE_RES_VIRT, VERR_INVALID_PARAMETER);
+ if (uAlignment == 0)
+ uAlignment = PAGE_SIZE;
+ AssertReturn(uAlignment == PAGE_SIZE || uAlignment == _2M || uAlignment == _4M, VERR_INVALID_PARAMETER);
+ if (R3PtrFixed != (RTR3PTR)-1)
+ AssertReturn(!(R3PtrFixed & (uAlignment - 1)), VERR_INVALID_PARAMETER);
+ AssertReturn(fProt != RTMEM_PROT_NONE, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fProt & ~(RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)), VERR_INVALID_PARAMETER);
+ AssertReturn(!(offSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(offSub < pMemToMap->cb, VERR_INVALID_PARAMETER);
+ AssertReturn(!(cbSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cbSub <= pMemToMap->cb, VERR_INVALID_PARAMETER);
+ AssertReturn((!offSub && !cbSub) || (offSub + cbSub) <= pMemToMap->cb, VERR_INVALID_PARAMETER);
+ if (R0Process == NIL_RTR0PROCESS)
+ R0Process = RTR0ProcHandleSelf();
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* adjust the request to simplify the native code. */
+ if (offSub == 0 && cbSub == pMemToMap->cb)
+ cbSub = 0;
+
+ /* do the mapping. */
+ rc = rtR0MemObjNativeMapUser(&pNew, pMemToMap, R3PtrFixed, uAlignment, fProt, R0Process, offSub, cbSub, pszTag);
+ if (RT_SUCCESS(rc))
+ {
+ /* link it. */
+ rc = rtR0MemObjLink(pMemToMap, pNew);
+ if (RT_SUCCESS(rc))
+ *pMemObj = pNew;
+ else
+ {
+ /* damn, out of memory. bail out. */
+ int rc2 = rtR0MemObjNativeFree(pNew);
+ AssertRC(rc2);
+ pNew->u32Magic++;
+ pNew->enmType = RTR0MEMOBJTYPE_END;
+ RTMemFree(pNew);
+ }
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjMapUserExTag);
+
+
+RTR0DECL(int) RTR0MemObjProtect(RTR0MEMOBJ hMemObj, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ PRTR0MEMOBJINTERNAL pMemObj;
+ int rc;
+
+ /* sanity checks. */
+ pMemObj = (PRTR0MEMOBJINTERNAL)hMemObj;
+ AssertPtrReturn(pMemObj, VERR_INVALID_HANDLE);
+ AssertReturn(pMemObj->u32Magic == RTR0MEMOBJ_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pMemObj->enmType > RTR0MEMOBJTYPE_INVALID && pMemObj->enmType < RTR0MEMOBJTYPE_END, VERR_INVALID_HANDLE);
+ AssertReturn(rtR0MemObjIsProtectable(pMemObj), VERR_INVALID_PARAMETER);
+ AssertReturn(!(offSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(offSub < pMemObj->cb, VERR_INVALID_PARAMETER);
+ AssertReturn(!(cbSub & PAGE_OFFSET_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cbSub <= pMemObj->cb, VERR_INVALID_PARAMETER);
+ AssertReturn(offSub + cbSub <= pMemObj->cb, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fProt & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC)), VERR_INVALID_PARAMETER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* do the job */
+ rc = rtR0MemObjNativeProtect(pMemObj, offSub, cbSub, fProt);
+ if (RT_SUCCESS(rc))
+ pMemObj->fFlags |= RTR0MEMOBJ_FLAGS_PROT_CHANGED; /* record it */
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTR0MemObjProtect);
+
diff --git a/src/VBox/Runtime/r0drv/mp-r0drv.h b/src/VBox/Runtime/r0drv/mp-r0drv.h
new file mode 100644
index 00000000..219a0e6a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/mp-r0drv.h
@@ -0,0 +1,95 @@
+/* $Id: mp-r0drv.h $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, 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 IPRT_INCLUDED_SRC_r0drv_mp_r0drv_h
+#define IPRT_INCLUDED_SRC_r0drv_mp_r0drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/mp.h>
+
+RT_C_DECLS_BEGIN
+
+/**
+ * MP callback
+ *
+ * @param idCpu CPU id
+ * @param pvUser1 The first user argument.
+ * @param pvUser2 The second user argument.
+ */
+typedef DECLCALLBACKTYPE(void, FNMPWORKER,(RTCPUID idCpu, void *pvUser1, void *pvUser2));
+/** Pointer to a FNMPWORKER(). */
+typedef FNMPWORKER *PFNMPWORKER;
+
+/**
+ * RTMpOn* argument packet used by the host specific callback
+ * wrapper functions.
+ */
+typedef struct RTMPARGS
+{
+ PFNMPWORKER pfnWorker;
+ void *pvUser1;
+ void *pvUser2;
+ RTCPUID idCpu;
+ RTCPUID idCpu2;
+ uint32_t volatile cHits;
+#ifdef RT_OS_WINDOWS
+ /** Turns out that KeFlushQueuedDpcs doesn't necessarily wait till all
+ * callbacks are done. So, do reference counting to make sure we don't free
+ * this structure befor all CPUs have completely handled their requests. */
+ int32_t volatile cRefs;
+#endif
+#ifdef RT_OS_LINUX
+ PRTCPUSET pWorkerSet;
+#endif
+} RTMPARGS;
+/** Pointer to a RTMpOn* argument packet. */
+typedef RTMPARGS *PRTMPARGS;
+
+/* Called from initterm-r0drv.cpp: */
+DECLHIDDEN(int) rtR0MpNotificationInit(void);
+DECLHIDDEN(void) rtR0MpNotificationTerm(void);
+
+/* The following is only relevant when using mpnotifcation-r0drv.cpp: */
+DECLHIDDEN(int) rtR0MpNotificationNativeInit(void);
+DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void);
+DECLHIDDEN(void) rtMpNotificationDoCallbacks(RTMPEVENT enmEvent, RTCPUID idCpu);
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_mp_r0drv_h */
+
diff --git a/src/VBox/Runtime/r0drv/mpnotification-r0drv.c b/src/VBox/Runtime/r0drv/mpnotification-r0drv.c
new file mode 100644
index 00000000..b958bd76
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/mpnotification-r0drv.c
@@ -0,0 +1,332 @@
+/* $Id: mpnotification-r0drv.c $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, Event Notifications.
+ */
+
+/*
+ * 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 <iprt/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "r0drv/mp-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Notification registration record tracking
+ * RTMpRegisterNotification() calls.
+ */
+typedef struct RTMPNOTIFYREG
+{
+ /** Pointer to the next record. */
+ struct RTMPNOTIFYREG * volatile pNext;
+ /** The callback. */
+ PFNRTMPNOTIFICATION pfnCallback;
+ /** The user argument. */
+ void *pvUser;
+ /** Bit mask indicating whether we've done this callback or not. */
+ uint8_t bmDone[sizeof(void *)];
+} RTMPNOTIFYREG;
+/** Pointer to a registration record. */
+typedef RTMPNOTIFYREG *PRTMPNOTIFYREG;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The spinlock protecting the list. */
+static RTSPINLOCK volatile g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
+/** List of callbacks, in registration order. */
+static PRTMPNOTIFYREG volatile g_pRTMpCallbackHead = NULL;
+/** The current done bit. */
+static uint32_t volatile g_iRTMpDoneBit;
+/** The list generation.
+ * This is increased whenever the list has been modified. The callback routine
+ * make use of this to avoid having restart at the list head after each callback. */
+static uint32_t volatile g_iRTMpGeneration;
+
+
+
+
+/**
+ * This is called by the native code.
+ *
+ * @param idCpu The CPU id the event applies to.
+ * @param enmEvent The event.
+ */
+DECLHIDDEN(void) rtMpNotificationDoCallbacks(RTMPEVENT enmEvent, RTCPUID idCpu)
+{
+ PRTMPNOTIFYREG pCur;
+ RTSPINLOCK hSpinlock;
+
+ /*
+ * This is a little bit tricky as we cannot be holding the spinlock
+ * while calling the callback. This means that the list might change
+ * while we're walking it, and that multiple events might be running
+ * concurrently (depending on the OS).
+ *
+ * So, the first measure is to employ a 32-bitmask for each
+ * record where we'll use a bit that rotates for each call to
+ * this function to indicate which records that has been
+ * processed. This will take care of both changes to the list
+ * and a reasonable amount of concurrent events.
+ *
+ * In order to avoid having to restart the list walks for every
+ * callback we make, we'll make use a list generation number that is
+ * incremented everytime the list is changed. So, if it remains
+ * unchanged over a callback we can safely continue the iteration.
+ */
+ uint32_t iDone = ASMAtomicIncU32(&g_iRTMpDoneBit);
+ iDone %= RT_SIZEOFMEMB(RTMPNOTIFYREG, bmDone) * 8;
+
+ hSpinlock = g_hRTMpNotifySpinLock;
+ if (hSpinlock == NIL_RTSPINLOCK)
+ return;
+ RTSpinlockAcquire(hSpinlock);
+
+ /* Clear the bit. */
+ for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
+ ASMAtomicBitClear(&pCur->bmDone[0], iDone);
+
+ /* Iterate the records and perform the callbacks. */
+ do
+ {
+ uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTMpGeneration);
+
+ pCur = g_pRTMpCallbackHead;
+ while (pCur)
+ {
+ if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
+ {
+ PFNRTMPNOTIFICATION pfnCallback = pCur->pfnCallback;
+ void *pvUser = pCur->pvUser;
+ pCur = pCur->pNext;
+ RTSpinlockRelease(g_hRTMpNotifySpinLock);
+
+ pfnCallback(enmEvent, idCpu, pvUser);
+
+ /* carefully require the lock here, see RTR0MpNotificationTerm(). */
+ hSpinlock = g_hRTMpNotifySpinLock;
+ if (hSpinlock == NIL_RTSPINLOCK)
+ return;
+ RTSpinlockAcquire(hSpinlock);
+ if (ASMAtomicUoReadU32(&g_iRTMpGeneration) != iGeneration)
+ break;
+ }
+ else
+ pCur = pCur->pNext;
+ }
+ } while (pCur);
+
+ RTSpinlockRelease(hSpinlock);
+}
+
+
+
+RTDECL(int) RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
+{
+ PRTMPNOTIFYREG pCur;
+ PRTMPNOTIFYREG pNew;
+
+ /*
+ * Validation.
+ */
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ RTSpinlockAcquire(g_hRTMpNotifySpinLock);
+ for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
+ if ( pCur->pvUser == pvUser
+ && pCur->pfnCallback == pfnCallback)
+ break;
+ RTSpinlockRelease(g_hRTMpNotifySpinLock);
+ AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
+
+ /*
+ * Allocate a new record and attempt to insert it.
+ */
+ pNew = (PRTMPNOTIFYREG)RTMemAlloc(sizeof(*pNew));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+
+ pNew->pNext = NULL;
+ pNew->pfnCallback = pfnCallback;
+ pNew->pvUser = pvUser;
+ memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
+
+ RTSpinlockAcquire(g_hRTMpNotifySpinLock);
+
+ pCur = g_pRTMpCallbackHead;
+ if (!pCur)
+ g_pRTMpCallbackHead = pNew;
+ else
+ {
+ for (pCur = g_pRTMpCallbackHead; ; pCur = pCur->pNext)
+ if ( pCur->pvUser == pvUser
+ && pCur->pfnCallback == pfnCallback)
+ break;
+ else if (!pCur->pNext)
+ {
+ pCur->pNext = pNew;
+ pCur = NULL;
+ break;
+ }
+ }
+
+ ASMAtomicIncU32(&g_iRTMpGeneration);
+
+ RTSpinlockRelease(g_hRTMpNotifySpinLock);
+
+ /* duplicate? */
+ if (pCur)
+ {
+ RTMemFree(pCur);
+ AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpNotificationRegister);
+
+
+RTDECL(int) RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser)
+{
+ PRTMPNOTIFYREG pPrev;
+ PRTMPNOTIFYREG pCur;
+
+ /*
+ * Validation.
+ */
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertReturn(g_hRTMpNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
+ RT_ASSERT_INTS_ON();
+
+ /*
+ * Find and unlink the record from the list.
+ */
+ RTSpinlockAcquire(g_hRTMpNotifySpinLock);
+ pPrev = NULL;
+ for (pCur = g_pRTMpCallbackHead; pCur; pCur = pCur->pNext)
+ {
+ if ( pCur->pvUser == pvUser
+ && pCur->pfnCallback == pfnCallback)
+ break;
+ pPrev = pCur;
+ }
+ if (pCur)
+ {
+ if (pPrev)
+ pPrev->pNext = pCur->pNext;
+ else
+ g_pRTMpCallbackHead = pCur->pNext;
+ ASMAtomicIncU32(&g_iRTMpGeneration);
+ }
+ RTSpinlockRelease(g_hRTMpNotifySpinLock);
+
+ if (!pCur)
+ return VERR_NOT_FOUND;
+
+ /*
+ * Invalidate and free the record.
+ */
+ pCur->pNext = NULL;
+ pCur->pfnCallback = NULL;
+ RTMemFree(pCur);
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTMpNotificationDeregister);
+
+
+DECLHIDDEN(int) rtR0MpNotificationInit(void)
+{
+ int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTMpNotifySpinLock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTR0Mp");
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtR0MpNotificationNativeInit();
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ RTSpinlockDestroy(g_hRTMpNotifySpinLock);
+ g_hRTMpNotifySpinLock = NIL_RTSPINLOCK;
+ }
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtR0MpNotificationTerm(void)
+{
+ PRTMPNOTIFYREG pHead;
+ RTSPINLOCK hSpinlock = g_hRTMpNotifySpinLock;
+ AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
+
+ rtR0MpNotificationNativeTerm();
+
+ /* pick up the list and the spinlock. */
+ RTSpinlockAcquire(hSpinlock);
+ ASMAtomicWriteHandle(&g_hRTMpNotifySpinLock, NIL_RTSPINLOCK);
+ pHead = g_pRTMpCallbackHead;
+ g_pRTMpCallbackHead = NULL;
+ ASMAtomicIncU32(&g_iRTMpGeneration);
+ RTSpinlockRelease(hSpinlock);
+
+ /* free the list. */
+ while (pHead)
+ {
+ PRTMPNOTIFYREG pFree = pHead;
+ pHead = pHead->pNext;
+
+ pFree->pNext = NULL;
+ pFree->pfnCallback = NULL;
+ RTMemFree(pFree);
+ }
+
+ RTSpinlockDestroy(hSpinlock);
+}
+
diff --git a/src/VBox/Runtime/r0drv/netbsd/Makefile.kup b/src/VBox/Runtime/r0drv/netbsd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/netbsd/RTLogWriteStdOut-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/RTLogWriteStdOut-r0drv-netbsd.c
new file mode 100644
index 00000000..8f37516b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/RTLogWriteStdOut-r0drv-netbsd.c
@@ -0,0 +1,49 @@
+/* $Id: RTLogWriteStdOut-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Log To StdOut, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * 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 "the-netbsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/log.h>
+
+
+RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb)
+{
+ printf("%.*s", (int)cb, pch);
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/alloc-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/alloc-r0drv-netbsd.c
new file mode 100644
index 00000000..1c245576
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/alloc-r0drv-netbsd.c
@@ -0,0 +1,176 @@
+/* $Id: alloc-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * 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
+ * ---------------------------------------------------------------------------
+ *
+ * This code is based on:
+ *
+ * Copyright (c) 2014 Arto Huusko
+ *
+ * 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 "the-netbsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/param.h>
+
+#include "r0drv/alloc-r0drv.h"
+
+
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ size_t cbAllocated = cb;
+ PRTMEMHDR pHdr = NULL;
+
+ if (fFlags & RTMEMHDR_FLAG_ZEROED)
+ pHdr = kmem_zalloc(cb + sizeof(RTMEMHDR), KM_NOSLEEP);
+ else
+ pHdr = kmem_alloc(cb + sizeof(RTMEMHDR), KM_NOSLEEP);
+
+ if (RT_UNLIKELY(!pHdr))
+ return VERR_NO_MEMORY;
+
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = cbAllocated;
+ pHdr->cbReq = cb;
+
+ *ppHdr = pHdr;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr)
+{
+ pHdr->u32Magic += 1;
+
+ kmem_free(pHdr, pHdr->cb + sizeof(RTMEMHDR));
+}
+
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
+{
+ if (pv)
+ {
+ cb = round_page(cb);
+
+ paddr_t pa;
+ pmap_extract(pmap_kernel(), (vaddr_t)pv, &pa);
+
+ /*
+ * Reconstruct pglist to free the physical pages
+ */
+ struct pglist rlist;
+ TAILQ_INIT(&rlist);
+
+ for (paddr_t pa2 = pa ; pa2 < pa + cb ; pa2 += PAGE_SIZE) {
+ struct vm_page *page = PHYS_TO_VM_PAGE(pa2);
+ TAILQ_INSERT_TAIL(&rlist, page, pageq.queue);
+ }
+
+ /* Unmap */
+ pmap_kremove((vaddr_t)pv, cb);
+
+ /* Free the virtual space */
+ uvm_km_free(kernel_map, (vaddr_t)pv, cb, UVM_KMF_VAONLY);
+
+ /* Free the physical pages */
+ uvm_pglistfree(&rlist);
+ }
+}
+
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(pPhys);
+ Assert(cb > 0);
+
+ cb = round_page(cb);
+
+ vaddr_t virt = uvm_km_alloc(kernel_map, cb, 0,
+ UVM_KMF_VAONLY | UVM_KMF_WAITVA | UVM_KMF_CANFAIL);
+ if (virt == 0)
+ return NULL;
+
+ struct pglist rlist;
+
+ if (uvm_pglistalloc(cb, 0, (paddr_t)0xFFFFFFFF,
+ PAGE_SIZE, 0, &rlist, 1, 1) != 0)
+ {
+ uvm_km_free(kernel_map, virt, cb, UVM_KMF_VAONLY);
+ return NULL;
+ }
+
+ struct vm_page *page;
+ vaddr_t virt2 = virt;
+ TAILQ_FOREACH(page, &rlist, pageq.queue)
+ {
+ pmap_kenter_pa(virt2, VM_PAGE_TO_PHYS(page),
+ VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, 0);
+ virt2 += PAGE_SIZE;
+ }
+
+ page = TAILQ_FIRST(&rlist);
+ *pPhys = VM_PAGE_TO_PHYS(page);
+
+ return (void *)virt;
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/assert-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/assert-r0drv-netbsd.c
new file mode 100644
index 00000000..b8808ffb
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/assert-r0drv-netbsd.c
@@ -0,0 +1,95 @@
+/* $Id: assert-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-netbsd-kernel.h"
+
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/stdarg.h>
+
+#include "internal/assert.h"
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ printf("\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%d) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+ /** @todo implement rtR0AssertNativeMsg2V. */
+}
+
+
+RTR0DECL(void) RTR0AssertPanicSystem(void)
+{
+ panic("%s%s", g_szRTAssertMsg1, g_szRTAssertMsg2);
+
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/initterm-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/initterm-r0drv-netbsd.c
new file mode 100644
index 00000000..b00ff46c
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/initterm-r0drv-netbsd.c
@@ -0,0 +1,84 @@
+/* $Id: initterm-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Initialization & Termination, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-netbsd-kernel.h"
+
+#include <iprt/errcore.h>
+
+#include "internal/initterm.h"
+
+
+DECLHIDDEN(int) rtR0InitNative(void)
+{
+ /* nothing to do */
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0TermNative(void)
+{
+ /* nothing to undo */
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/memobj-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/memobj-r0drv-netbsd.c
new file mode 100644
index 00000000..b77e2854
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/memobj-r0drv-netbsd.c
@@ -0,0 +1,586 @@
+/* $Id: memobj-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen, Andriy Gapon, Arto Huusko.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net>
+ * Copyright (c) 2011 Andriy Gapon <avg@FreeBSD.org>
+ * Copyright (c) 2014 Arto Huusko
+ *
+ * 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 "the-netbsd-kernel.h"
+
+#include <iprt/memobj.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include "internal/memobj.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The NetBSD version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJNETBSD
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ size_t size;
+ struct pglist pglist;
+} RTR0MEMOBJNETBSD, *PRTR0MEMOBJNETBSD;
+
+
+typedef struct vm_map* vm_map_t;
+
+/**
+ * Gets the virtual memory map the specified object is mapped into.
+ *
+ * @returns VM map handle on success, NULL if no map.
+ * @param pMem The memory object.
+ */
+static vm_map_t rtR0MemObjNetBSDGetMap(PRTR0MEMOBJINTERNAL pMem)
+{
+ switch (pMem->enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ return kernel_map;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ return NULL; /* pretend these have no mapping atm. */
+
+ case RTR0MEMOBJTYPE_LOCK:
+ return pMem->u.Lock.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.Lock.R0Process)->p_vmspace->vm_map;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ return pMem->u.ResVirt.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.ResVirt.R0Process)->p_vmspace->vm_map;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ return pMem->u.Mapping.R0Process == NIL_RTR0PROCESS
+ ? kernel_map
+ : &((struct proc *)pMem->u.Mapping.R0Process)->p_vmspace->vm_map;
+
+ default:
+ return NULL;
+ }
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)pMem;
+ int rc;
+
+ switch (pMemNetBSD->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ {
+ kmem_free(pMemNetBSD->Core.pv, pMemNetBSD->Core.cb);
+ break;
+ }
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ {
+ /* Unmap */
+ pmap_kremove((vaddr_t)pMemNetBSD->Core.pv, pMemNetBSD->Core.cb);
+ /* Free the virtual space */
+ uvm_km_free(kernel_map, (vaddr_t)pMemNetBSD->Core.pv, pMemNetBSD->Core.cb, UVM_KMF_VAONLY);
+ /* Free the physical pages */
+ uvm_pglistfree(&pMemNetBSD->pglist);
+ break;
+ }
+ case RTR0MEMOBJTYPE_PHYS:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ {
+ /* Free the physical pages */
+ uvm_pglistfree(&pMemNetBSD->pglist);
+ break;
+ }
+ case RTR0MEMOBJTYPE_LOCK:
+ if (pMemNetBSD->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ {
+ uvm_map_pageable(
+ &((struct proc *)pMemNetBSD->Core.u.Lock.R0Process)->p_vmspace->vm_map,
+ (vaddr_t)pMemNetBSD->Core.pv,
+ ((vaddr_t)pMemNetBSD->Core.pv) + pMemNetBSD->Core.cb,
+ 1, 0);
+ }
+ break;
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ if (pMemNetBSD->Core.u.Lock.R0Process == NIL_RTR0PROCESS)
+ {
+ uvm_km_free(kernel_map, (vaddr_t)pMemNetBSD->Core.pv, pMemNetBSD->Core.cb, UVM_KMF_VAONLY);
+ }
+ break;
+ case RTR0MEMOBJTYPE_MAPPING:
+ if (pMemNetBSD->Core.u.Lock.R0Process == NIL_RTR0PROCESS)
+ {
+ pmap_kremove((vaddr_t)pMemNetBSD->Core.pv, pMemNetBSD->Core.cb);
+ uvm_km_free(kernel_map, (vaddr_t)pMemNetBSD->Core.pv, pMemNetBSD->Core.cb, UVM_KMF_VAONLY);
+ }
+ break;
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemNetBSD->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static int rtR0MemObjNetBSDAllocHelper(PRTR0MEMOBJNETBSD pMemNetBSD, size_t cb, bool fExecutable,
+ paddr_t VmPhysAddrHigh, bool fContiguous)
+{
+ /* Virtual space first */
+ vaddr_t virt = uvm_km_alloc(kernel_map, cb, 0, UVM_KMF_VAONLY | UVM_KMF_WAITVA | UVM_KMF_CANFAIL);
+ if (virt == 0)
+ return VERR_NO_MEMORY;
+
+ struct pglist *rlist = &pMemNetBSD->pglist;
+
+ int nsegs = fContiguous ? 1 : INT_MAX;
+
+ /* Physical pages */
+ if (uvm_pglistalloc(cb, 0, VmPhysAddrHigh, PAGE_SIZE, 0, rlist, nsegs, 1) != 0)
+ {
+ uvm_km_free(kernel_map, virt, cb, UVM_KMF_VAONLY);
+ return VERR_NO_MEMORY; /** @todo inaccurate status code */
+ }
+
+ /* Map */
+ struct vm_page *page;
+ vm_prot_t prot = VM_PROT_READ | VM_PROT_WRITE;
+ if (fExecutable)
+ prot |= VM_PROT_EXECUTE;
+ vaddr_t virt2 = virt;
+ TAILQ_FOREACH(page, rlist, pageq.queue)
+ {
+ pmap_kenter_pa(virt2, VM_PAGE_TO_PHYS(page), prot, 0);
+ virt2 += PAGE_SIZE;
+ }
+
+ pMemNetBSD->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /*?*/
+ pMemNetBSD->Core.pv = (void *)virt;
+ if (fContiguous)
+ {
+ page = TAILQ_FIRST(rlist);
+ pMemNetBSD->Core.u.Cont.Phys = VM_PAGE_TO_PHYS(page);
+ }
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_PAGE, NULL, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ void *pvMem = kmem_alloc(cb, KM_SLEEP);
+ if (pvMem)
+ {
+ if (fExecutable)
+ pmap_protect(pmap_kernel(), (vaddr_t)pvMem, (vaddr_t)pvMem + cb, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE);
+
+ pMemNetBSD->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ pMemNetBSD->Core.pv = pvMem;
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemNetBSD->Core);
+ return VERR_NO_PAGE_MEMORY;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_LOW, NULL, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ int rc = rtR0MemObjNetBSDAllocHelper(pMemNetBSD, cb, fExecutable, _4G - 1, false /*fContiguous*/);
+ if (RT_SUCCESS(rc))
+ {
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemNetBSD->Core);
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_CONT, NULL, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ int rc = rtR0MemObjNetBSDAllocHelper(pMemNetBSD, cb, fExecutable, _4G - 1, true /*fContiguous*/);
+ if (RT_SUCCESS(rc))
+ {
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemNetBSD->Core);
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+static int rtR0MemObjNetBSDAllocPhysPages(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJTYPE enmType, size_t cb,
+ RTHCPHYS PhysHighest, size_t uAlignment, bool fContiguous, const char *pszTag)
+{
+ /* create the object. */
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), enmType, NULL, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ paddr_t const VmPhysAddrHigh = PhysHighest != NIL_RTHCPHYS ? PhysHighest : ~(paddr_t)0;
+ int const nsegs = fContiguous ? 1 : INT_MAX;
+ int rc = uvm_pglistalloc(cb, 0, VmPhysAddrHigh, uAlignment, 0, &pMemNetBSD->pglist, nsegs, 1);
+ if (!rc)
+ {
+ pMemNetBSD->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /*?*/
+ if (fContiguous)
+ {
+ Assert(enmType == RTR0MEMOBJTYPE_PHYS);
+ const struct vm_page * const pg = TAILQ_FIRST(&pMemNetBSD->pglist);
+ pMemNetBSD->Core.u.Phys.PhysBase = VM_PAGE_TO_PHYS(pg);
+ pMemNetBSD->Core.u.Phys.fAllocated = true;
+ }
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemNetBSD->Core);
+ return VERR_NO_PAGE_MEMORY;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment,
+ const char *pszTag)
+{
+ return rtR0MemObjNetBSDAllocPhysPages(ppMem, RTR0MEMOBJTYPE_PHYS, cb, PhysHighest, uAlignment, true, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ return rtR0MemObjNetBSDAllocPhysPages(ppMem, RTR0MEMOBJTYPE_PHYS_NC, cb, PhysHighest, PAGE_SIZE, false, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy,
+ const char *pszTag)
+{
+ AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE, VERR_NOT_SUPPORTED);
+
+ /* create the object. */
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ /* there is no allocation here, it needs to be mapped somewhere first. */
+ pMemNetBSD->Core.u.Phys.fAllocated = false;
+ pMemNetBSD->Core.u.Phys.PhysBase = Phys;
+ pMemNetBSD->Core.u.Phys.uCachePolicy = uCachePolicy;
+ TAILQ_INIT(&pMemNetBSD->pglist);
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_LOCK,
+ (void *)R3Ptr, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ int rc = uvm_map_pageable(&((struct proc *)R0Process)->p_vmspace->vm_map, R3Ptr, R3Ptr + cb,
+ 0 /*new_pageable*/, 0 /*lockflags*/);
+ if (!rc)
+ {
+ pMemNetBSD->Core.u.Lock.R0Process = R0Process;
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemNetBSD->Core);
+ return VERR_LOCK_FAILED;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ /* Kernel memory (always?) wired; all memory allocated by vbox code is? */
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_LOCK, pv, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ pMemNetBSD->Core.u.Lock.R0Process = NIL_RTR0PROCESS;
+ pMemNetBSD->Core.pv = pv;
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ const char *pszTag)
+{
+ if (pvFixed != (void *)-1)
+ {
+ /* can we support this? or can we assume the virtual space is already reserved? */
+ printf("reserve specified kernel virtual address not supported\n");
+ return VERR_NOT_SUPPORTED;
+ }
+
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_RES_VIRT,
+ NULL, cb, pszTag);
+ if (pMemNetBSD)
+ {
+ vaddr_t virt = uvm_km_alloc(kernel_map, cb, uAlignment, UVM_KMF_VAONLY | UVM_KMF_WAITVA | UVM_KMF_CANFAIL);
+ if (virt != 0)
+ {
+ pMemNetBSD->Core.u.ResVirt.R0Process = NIL_RTR0PROCESS;
+ pMemNetBSD->Core.pv = (void *)virt;
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemNetBSD->Core);
+ return VERR_NO_MEMORY;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ RT_NOREF(ppMem, R3PtrFixed, cb, uAlignment, R0Process, pszTag);
+ printf("NativeReserveUser\n");
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ if (pvFixed != (void *)-1)
+ {
+ /* can we support this? or can we assume the virtual space is already reserved? */
+ printf("map to specified kernel virtual address not supported\n");
+ return VERR_NOT_SUPPORTED;
+ }
+
+ PRTR0MEMOBJNETBSD pMemNetBSD0 = (PRTR0MEMOBJNETBSD)pMemToMap;
+ if ((pMemNetBSD0->Core.enmType != RTR0MEMOBJTYPE_PHYS)
+ && (pMemNetBSD0->Core.enmType != RTR0MEMOBJTYPE_PHYS_NC))
+ {
+ printf("memory to map is not physical\n");
+ return VERR_NOT_SUPPORTED;
+ }
+ size_t sz = cbSub > 0 ? cbSub : pMemNetBSD0->Core.cb;
+
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)rtR0MemObjNew(sizeof(*pMemNetBSD), RTR0MEMOBJTYPE_MAPPING, NULL, sz, pszTag);
+
+ vaddr_t virt = uvm_km_alloc(kernel_map, sz, uAlignment, UVM_KMF_VAONLY | UVM_KMF_WAITVA | UVM_KMF_CANFAIL);
+ if (virt != 0)
+ {
+ vm_prot_t prot = 0;
+ if (fProt & RTMEM_PROT_READ)
+ prot |= VM_PROT_READ;
+ if (fProt & RTMEM_PROT_WRITE)
+ prot |= VM_PROT_WRITE;
+ if (fProt & RTMEM_PROT_EXEC)
+ prot |= VM_PROT_EXECUTE;
+
+ struct vm_page *page;
+ vaddr_t virt2 = virt;
+ size_t map_pos = 0;
+ TAILQ_FOREACH(page, &pMemNetBSD0->pglist, pageq.queue)
+ {
+ if (map_pos >= offSub)
+ {
+ if (cbSub > 0 && (map_pos >= offSub + cbSub))
+ break;
+
+ pmap_kenter_pa(virt2, VM_PAGE_TO_PHYS(page), prot, 0);
+ virt2 += PAGE_SIZE;
+ }
+ map_pos += PAGE_SIZE;
+ }
+
+ pMemNetBSD->Core.pv = (void *)virt;
+ pMemNetBSD->Core.u.Mapping.R0Process = NIL_RTR0PROCESS;
+ *ppMem = &pMemNetBSD->Core;
+ return VINF_SUCCESS;
+ }
+
+ rtR0MemObjDelete(&pMemNetBSD->Core);
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ RT_NOREF(ppMem, pMemToMap, R3PtrFixed, uAlignment, fProt, R0Process, offSub, cbSub, pszTag);
+ printf("NativeMapUser\n");
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ vm_map_t const pVmMap = rtR0MemObjNetBSDGetMap(pMem);
+ if (pVmMap)
+ {
+ vaddr_t const AddrStart = (vaddr_t)pMem->pv + offSub;
+ vm_prot_t ProtectionFlags = 0;
+ if (fProt & RTMEM_PROT_READ)
+ ProtectionFlags |= UVM_PROT_R;
+ if (fProt & RTMEM_PROT_WRITE)
+ ProtectionFlags |= UVM_PROT_W;
+ if (fProt & RTMEM_PROT_EXEC)
+ ProtectionFlags |= UVM_PROT_X;
+
+ int rc = uvm_map_protect(pVmMap, AddrStart, AddrStart + cbSub, ProtectionFlags, 0);
+ if (!rc)
+ return VINF_SUCCESS;
+ return RTErrConvertFromErrno(rc);
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ PRTR0MEMOBJNETBSD pMemNetBSD = (PRTR0MEMOBJNETBSD)pMem;
+
+ switch (pMemNetBSD->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ {
+ vaddr_t va = (vaddr_t)pMemNetBSD->Core.pv + ptoa(iPage);
+ paddr_t pa = 0;
+ pmap_extract(pmap_kernel(), va, &pa);
+ return pa;
+ }
+ case RTR0MEMOBJTYPE_CONT:
+ return pMemNetBSD->Core.u.Cont.Phys + ptoa(iPage);
+ case RTR0MEMOBJTYPE_PHYS:
+ return pMemNetBSD->Core.u.Phys.PhysBase + ptoa(iPage);
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ {
+ struct vm_page *page;
+ size_t i = 0;
+ TAILQ_FOREACH(page, &pMemNetBSD->pglist, pageq.queue)
+ {
+ if (i == iPage)
+ break;
+ i++;
+ }
+ return VM_PAGE_TO_PHYS(page);
+ }
+ case RTR0MEMOBJTYPE_LOCK:
+ case RTR0MEMOBJTYPE_MAPPING:
+ {
+ pmap_t pmap;
+ if (pMem->u.Lock.R0Process == NIL_RTR0PROCESS)
+ pmap = pmap_kernel();
+ else
+ pmap = ((struct proc *)pMem->u.Lock.R0Process)->p_vmspace->vm_map.pmap;
+ vaddr_t va = (vaddr_t)pMemNetBSD->Core.pv + ptoa(iPage);
+ paddr_t pa = 0;
+ pmap_extract(pmap, va, &pa);
+ return pa;
+ }
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ return NIL_RTHCPHYS;
+ default:
+ return NIL_RTHCPHYS;
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/netbsd/memuserkernel-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/memuserkernel-r0drv-netbsd.c
new file mode 100644
index 00000000..e2704beb
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/memuserkernel-r0drv-netbsd.c
@@ -0,0 +1,92 @@
+/* $Id: memuserkernel-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - User & Kernel Memory, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * 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-netbsd-kernel.h"
+
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+
+
+RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb)
+{
+ int rc = copyin((const void *)R3PtrSrc, pvDst, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb)
+{
+ int rc = copyout(pvSrc, (void *)R3PtrDst, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr)
+{
+ return R3Ptr < VM_MAXUSER_ADDRESS;
+}
+
+
+RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv)
+{
+ return (uintptr_t)pv >= VM_MAXUSER_ADDRESS;
+}
+
+
+RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void)
+{
+ return true;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb)
+{
+ return VERR_NOT_SUPPORTED;
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/mp-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/mp-r0drv-netbsd.c
new file mode 100644
index 00000000..3aef0355
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/mp-r0drv-netbsd.c
@@ -0,0 +1,53 @@
+/* $Id: mp-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * 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 "the-netbsd-kernel.h"
+
+#include <iprt/mp.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#include <iprt/cpuset.h>
+#include "r0drv/mp-r0drv.h"
+
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ return cpu_number();
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/process-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/process-r0drv-netbsd.c
new file mode 100644
index 00000000..1bd43674
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/process-r0drv-netbsd.c
@@ -0,0 +1,83 @@
+/* $Id: process-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Process Management, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-netbsd-kernel.h"
+
+#include <iprt/process.h>
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ struct proc *pSelf = curproc;
+ return pSelf->p_pid;
+}
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ return (RTR0PROCESS)curproc;
+}
+
diff --git a/src/VBox/Runtime/r0drv/netbsd/semevent-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/semevent-r0drv-netbsd.c
new file mode 100644
index 00000000..2c0a6e58
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/semevent-r0drv-netbsd.c
@@ -0,0 +1,294 @@
+/* $Id: semevent-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Single Release Event Semaphores, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 RTSEMEVENT_WITHOUT_REMAPPING
+#include "the-netbsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+
+#include "sleepqueue-r0drv-netbsd.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * NetBSD event semaphore.
+ */
+typedef struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The object status - !0 when signaled and 0 when reset. */
+ uint32_t volatile fState;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+ AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
+
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->fState = 0;
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Retains a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventBsdRetain(PRTSEMEVENTINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < 100000); NOREF(cRefs);
+}
+
+
+/**
+ * Releases a reference to the event semaphore.
+ *
+ * @param pThis The event semaphore.
+ */
+DECLINLINE(void) rtR0SemEventBsdRelease(PRTSEMEVENTINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ RTMemFree(pThis);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC);
+ ASMAtomicWriteU32(&pThis->fState, 0);
+ rtR0SemBsdBroadcast(pThis);
+ rtR0SemEventBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ rtR0SemEventBsdRetain(pThis);
+
+ /*
+ * Signal the event object.
+ */
+ ASMAtomicWriteU32(&pThis->fState, 1);
+ rtR0SemBsdSignal(pThis);
+ rtR0SemEventBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ int rc;
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ rtR0SemEventBsdRetain(pThis);
+
+ /*
+ * Try grab the event without setting up the wait.
+ */
+ if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1))
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ RTR0SEMBSDSLEEP Wait;
+ rc = rtR0SemBsdWaitInit(&Wait, fFlags, uTimeout, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ rtR0SemBsdWaitPrepare(&Wait);
+
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1))
+ rc = VINF_SUCCESS;
+ else if (rtR0SemBsdWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemBsdWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemBsdWaitDoIt(&Wait);
+ continue;
+ }
+ }
+ break;
+ }
+
+ rtR0SemBsdWaitDelete(&Wait);
+ }
+ }
+
+ rtR0SemEventBsdRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitEx);
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ return 1000000000 / hz;
+}
+
+RTR0DECL(bool) RTSemEventIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/netbsd/semeventmulti-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/semeventmulti-r0drv-netbsd.c
new file mode 100644
index 00000000..84c2e726
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/semeventmulti-r0drv-netbsd.c
@@ -0,0 +1,360 @@
+/* $Id: semeventmulti-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#include "the-netbsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/lockvalidator.h>
+
+#include "sleepqueue-r0drv-netbsd.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name fStateAndGen values
+ * @{ */
+/** The state bit number. */
+#define RTSEMEVENTMULTIBSD_STATE_BIT 0
+/** The state mask. */
+#define RTSEMEVENTMULTIBSD_STATE_MASK RT_BIT_32(RTSEMEVENTMULTIBSD_STATE_BIT)
+/** The generation mask. */
+#define RTSEMEVENTMULTIBSD_GEN_MASK ~RTSEMEVENTMULTIBSD_STATE_MASK
+/** The generation shift. */
+#define RTSEMEVENTMULTIBSD_GEN_SHIFT 1
+/** The initial variable value. */
+#define RTSEMEVENTMULTIBSD_STATE_GEN_INIT UINT32_C(0xfffffffc)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * NetBSD multiple release event semaphore.
+ */
+typedef struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The object state bit and generation counter.
+ * The generation counter is incremented every time the object is
+ * signalled. */
+ uint32_t volatile fStateAndGen;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis;
+
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->fStateAndGen = RTSEMEVENTMULTIBSD_STATE_GEN_INIT;
+ pThis->cRefs = 1;
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiBsdRetain(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiBsdRelease(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ {
+ Assert(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC);
+ RTMemFree(pThis);
+ }
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ Assert(pThis->cRefs > 0);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC);
+ ASMAtomicAndU32(&pThis->fStateAndGen, RTSEMEVENTMULTIBSD_GEN_MASK);
+ rtR0SemBsdBroadcast(pThis);
+ rtR0SemEventMultiBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ uint32_t fNew;
+ uint32_t fOld;
+
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiBsdRetain(pThis);
+
+ /*
+ * Signal the event object. The cause of the parnoia here is racing to try
+ * deal with racing RTSemEventMultiSignal calls (should probably be
+ * forbidden, but it's relatively easy to handle).
+ */
+ do
+ {
+ fNew = fOld = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ fNew += 1 << RTSEMEVENTMULTIBSD_GEN_SHIFT;
+ fNew |= RTSEMEVENTMULTIBSD_STATE_MASK;
+ }
+ while (!ASMAtomicCmpXchgU32(&pThis->fStateAndGen, fNew, fOld));
+
+ rtR0SemBsdBroadcast(pThis);
+ rtR0SemEventMultiBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiBsdRetain(pThis);
+
+ /*
+ * Reset it.
+ */
+ ASMAtomicAndU32(&pThis->fStateAndGen, ~RTSEMEVENTMULTIBSD_STATE_MASK);
+
+ rtR0SemEventMultiBsdRelease(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param uTimeout See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventMultiBsdWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ uint32_t fOrgStateAndGen;
+ int rc;
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiBsdRetain(pThis);
+
+ /*
+ * Is the event already signalled or do we have to wait?
+ */
+ fOrgStateAndGen = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ if (fOrgStateAndGen & RTSEMEVENTMULTIBSD_STATE_MASK)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ RTR0SEMBSDSLEEP Wait;
+ rc = rtR0SemBsdWaitInit(&Wait, fFlags, uTimeout, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ rtR0SemBsdWaitPrepare(&Wait);
+
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (ASMAtomicUoReadU32(&pThis->fStateAndGen) != fOrgStateAndGen)
+ rc = VINF_SUCCESS;
+ else if (rtR0SemBsdWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemBsdWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemBsdWaitDoIt(&Wait);
+ continue;
+ }
+ }
+ break;
+ }
+
+ rtR0SemBsdWaitDelete(&Wait);
+ }
+ }
+
+ rtR0SemEventMultiBsdRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitEx);
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiWaitExDebug);
+
+
+RTDECL(uint32_t) RTSemEventMultiGetResolution(void)
+{
+ return rtR0SemBsdWaitGetResolution();
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiGetResolution);
+
+
+RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
+{
+ /** @todo check the code... */
+ return false;
+}
+RT_EXPORT_SYMBOL(RTSemEventMultiIsSignalSafe);
+
diff --git a/src/VBox/Runtime/r0drv/netbsd/semfastmutex-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/semfastmutex-r0drv-netbsd.c
new file mode 100644
index 00000000..2718d5d8
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/semfastmutex-r0drv-netbsd.c
@@ -0,0 +1,146 @@
+/* $Id: semfastmutex-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-netbsd-kernel.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/errcore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the NetBSD (sleep) mutex.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The NetBSD shared/exclusive lock mutex. */
+ krwlock_t Mtx;
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ AssertCompile(sizeof(RTSEMFASTMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phFastMtx, VERR_INVALID_POINTER);
+
+ PRTSEMFASTMUTEXINTERNAL pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ rw_init(&pThis->Mtx);
+
+ *phFastMtx = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ rw_destroy(&pThis->Mtx);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ rw_enter(&pThis->Mtx, RW_WRITER);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ rw_exit(&pThis->Mtx);
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/semmutex-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/semmutex-r0drv-netbsd.c
new file mode 100644
index 00000000..16e8f272
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/semmutex-r0drv-netbsd.c
@@ -0,0 +1,228 @@
+/* $Id: semmutex-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Mutex Semaphores, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTSEMMUTEX_WITHOUT_REMAPPING
+#include "the-netbsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the NetBSD (sleep) mutex.
+ */
+typedef struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The NetBSD shared/exclusive lock mutex. */
+ struct sx SxLock;
+} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ AssertCompile(sizeof(RTSEMMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phMutexSem, VERR_INVALID_POINTER);
+
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ sx_init_flags(&pThis->SxLock, "IPRT Mutex Semaphore", SX_RECURSE);
+
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+
+ sx_destroy(&pThis->SxLock);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ int rc;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ sx_xlock(&pThis->SxLock);
+ rc = VINF_SUCCESS;
+ }
+ else if (!cMillies)
+ {
+ if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_TIMEOUT;
+ }
+ /*
+ * GROSS HACK: poll implementation of timeout.
+ */
+ /** @todo Implement timeouts in RTSemMutexRequest. */
+ else if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ {
+ uint64_t StartTS = RTTimeSystemMilliTS();
+ rc = VERR_TIMEOUT;
+ do
+ {
+ RTThreadSleep(1);
+ if (sx_try_xlock(&pThis->SxLock))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ } while (RTTimeSystemMilliTS() - StartTS < cMillies);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequest(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ int rc;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ if (cMillies == RT_INDEFINITE_WAIT)
+ {
+ if (!sx_xlock_sig(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_INTERRUPTED;
+ }
+ else if (!cMillies)
+ {
+ if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_TIMEOUT;
+ }
+ /*
+ * GROSS HACK: poll implementation of timeout.
+ */
+ /** @todo Implement timeouts and interrupt checks in
+ * RTSemMutexRequestNoResume. */
+ else if (sx_try_xlock(&pThis->SxLock))
+ rc = VINF_SUCCESS;
+ else
+ {
+ uint64_t StartTS = RTTimeSystemMilliTS();
+ rc = VERR_TIMEOUT;
+ do
+ {
+ RTThreadSleep(1);
+ if (sx_try_xlock(&pThis->SxLock))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ } while (RTTimeSystemMilliTS() - StartTS < cMillies);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ sx_xunlock(&pThis->SxLock);
+ return VINF_SUCCESS;
+}
+
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), false);
+
+ return sx_xlocked(&pThis->SxLock);
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/sleepqueue-r0drv-netbsd.h b/src/VBox/Runtime/r0drv/netbsd/sleepqueue-r0drv-netbsd.h
new file mode 100644
index 00000000..31cc97d4
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/sleepqueue-r0drv-netbsd.h
@@ -0,0 +1,300 @@
+/* $Id: sleepqueue-r0drv-netbsd.h $ */
+/** @file
+ * IPRT - NetBSD Ring-0 Driver Helpers for Abstracting Sleep Queues,
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_netbsd_sleepqueue_r0drv_netbsd_h
+#define IPRT_INCLUDED_SRC_r0drv_netbsd_sleepqueue_r0drv_netbsd_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "the-netbsd-kernel.h"
+
+#include <iprt/asm-math.h>
+#include <iprt/err.h>
+#include <iprt/time.h>
+
+static syncobj_t vbox_syncobj = {
+ SOBJ_SLEEPQ_SORTED,
+ sleepq_unsleep,
+ sleepq_changepri,
+ sleepq_lendpri,
+ syncobj_noowner,
+};
+
+/**
+ * Kernel mode NetBSD wait state structure.
+ */
+typedef struct RTR0SEMBSDSLEEP
+{
+ /** The absolute timeout given as nano seconds since the start of the
+ * monotonic clock. */
+ uint64_t uNsAbsTimeout;
+ /** The timeout in ticks. Updated after waiting. */
+ int iTimeout;
+ /** Set if it's an indefinite wait. */
+ bool fIndefinite;
+ /** Set if we've already timed out.
+ * Set by rtR0SemBsdWaitDoIt and read by rtR0SemBsdWaitHasTimedOut. */
+ bool fTimedOut;
+ /** Flag whether the wait was interrupted. */
+ bool fInterrupted;
+ /** flag whether the wait is interruptible or not. */
+ bool fInterruptible;
+ /** Opaque wait channel id. */
+ wchan_t wchan;
+ sleepq_t *sq;
+ kmutex_t *sq_lock;
+} RTR0SEMBSDSLEEP;
+/** Pointer to a NetBSD wait state. */
+typedef RTR0SEMBSDSLEEP *PRTR0SEMBSDSLEEP;
+
+
+/**
+ * Updates the timeout of the NetBSD wait.
+ *
+ * @returns RTSEMWAIT_FLAGS_INDEFINITE if the timeout value is too big.
+ * 0 otherwise
+ * @param pWait The wait structure.
+ * @param uTimeout The relative timeout in nanoseconds.
+ */
+DECLINLINE(void) rtR0SemBsdWaitUpdateTimeout(PRTR0SEMBSDSLEEP pWait)
+{
+ /* Convert absolute timeout to ticks */
+ uint64_t now = RTTimeNanoTS();
+ if (now >= pWait->uNsAbsTimeout) {
+ pWait->iTimeout = 0;
+ } else {
+ uint64_t nanos = pWait->uNsAbsTimeout - now;
+ pWait->iTimeout = hz * nanos / 1000000000;
+ /* for 1ms wait, wait at least one tick ?? */
+ if ((pWait->iTimeout == 0) && (nanos >= 1000000)) {
+ pWait->iTimeout = 1;
+ }
+ }
+}
+
+/**
+ * Initializes a wait.
+ *
+ * The caller MUST check the wait condition BEFORE calling this function or the
+ * timeout logic will be flawed.
+ *
+ * @returns VINF_SUCCESS or VERR_TIMEOUT.
+ * @param pWait The wait structure.
+ * @param fFlags The wait flags.
+ * @param uTimeout The timeout.
+ * @param pvWaitChan The opaque wait channel.
+ */
+DECLINLINE(int) rtR0SemBsdWaitInit(PRTR0SEMBSDSLEEP pWait, uint32_t fFlags, uint64_t uTimeout,
+ void *pvWaitChan)
+{
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE) {
+ pWait->fIndefinite = true;
+ pWait->iTimeout = 0;
+ pWait->uNsAbsTimeout = 0;
+ } else {
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE) {
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS) {
+ pWait->uNsAbsTimeout = uTimeout * 1000000 + RTTimeSystemNanoTS();
+ } else {
+ pWait->uNsAbsTimeout = uTimeout + RTTimeSystemNanoTS();
+ }
+ } else {
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS) {
+ pWait->uNsAbsTimeout = uTimeout * 1000000;
+ } else {
+ pWait->uNsAbsTimeout = uTimeout;
+ }
+ }
+ rtR0SemBsdWaitUpdateTimeout(pWait);
+ if (pWait->iTimeout == 0) {
+ return VERR_TIMEOUT;
+ }
+ }
+
+ pWait->fTimedOut = false;
+ /*
+ * Initialize the wait queue related bits.
+ */
+ pWait->fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE
+ ? true : false;
+ pWait->fInterrupted = false;
+ pWait->wchan = pvWaitChan;
+ pWait->sq = NULL;
+ pWait->sq_lock = NULL;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Prepares the next wait.
+ *
+ * This must be called before rtR0SemBsdWaitDoIt, and the caller should check
+ * the exit conditions inbetween the two calls.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemBsdWaitPrepare(PRTR0SEMBSDSLEEP pWait)
+{
+ pWait->sq = sleeptab_lookup(&sleeptab, pWait->wchan, &pWait->sq_lock);
+}
+
+/**
+ * Do the actual wait.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemBsdWaitDoIt(PRTR0SEMBSDSLEEP pWait)
+{
+#if __NetBSD_Prereq__(9,99,57)
+#define sleepq_enqueue(sq, wchan, wmesg, sobj) \
+ sleepq_enqueue((sq), (wchan), (wmesg), (sobj), true)
+#endif
+
+ sleepq_enter(pWait->sq, curlwp, pWait->sq_lock);
+ sleepq_enqueue(pWait->sq, pWait->wchan, "VBoxIS", &vbox_syncobj);
+
+ pWait->sq = NULL;
+ pWait->sq_lock = NULL;
+
+ int error = sleepq_block(pWait->iTimeout, pWait->fInterruptible
+#if __NetBSD_Prereq__(9,99,98) /* a bit later, actually... */
+ , &vbox_syncobj
+#endif
+ );
+ if (error == EWOULDBLOCK) {
+ if (!pWait->fIndefinite) {
+ pWait->fTimedOut = true;
+ }
+ } else if (error == ERESTART) {
+ if (pWait->fInterruptible) {
+ pWait->fInterrupted = true;
+ } else if (!pWait->fIndefinite) {
+ rtR0SemBsdWaitUpdateTimeout(pWait);
+ if (pWait->iTimeout == 0) {
+ pWait->fTimedOut = true;
+ }
+ }
+ } else if (error == EINTR) {
+ if (pWait->fInterruptible) {
+ pWait->fInterrupted = true;
+ } else if (!pWait->fIndefinite) {
+ rtR0SemBsdWaitUpdateTimeout(pWait);
+ if (pWait->iTimeout == 0) {
+ pWait->fTimedOut = true;
+ }
+ }
+ } else if (error) {
+ AssertMsgFailed(("sleepq_block -> %d\n", error));
+ }
+}
+
+
+/**
+ * Checks if a NetBSD wait was interrupted.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ * @remarks This shall be called before the first rtR0SemLnxWaitDoIt().
+ */
+DECLINLINE(bool) rtR0SemBsdWaitWasInterrupted(PRTR0SEMBSDSLEEP pWait)
+{
+ return pWait->fInterrupted;
+}
+
+
+/**
+ * Checks if a NetBSD wait has timed out.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ */
+DECLINLINE(bool) rtR0SemBsdWaitHasTimedOut(PRTR0SEMBSDSLEEP pWait)
+{
+ return pWait->fTimedOut;
+}
+
+
+/**
+ * Deletes a NetBSD wait.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemBsdWaitDelete(PRTR0SEMBSDSLEEP pWait)
+{
+ if (pWait->sq_lock != NULL) {
+ mutex_spin_exit(pWait->sq_lock);
+ pWait->sq = NULL;
+ pWait->sq_lock = NULL;
+ }
+}
+
+
+/**
+ * Signals the wait channel.
+ *
+ * @param pvWaitChan The opaque wait channel handle.
+ */
+DECLINLINE(void) rtR0SemBsdSignal(void *pvWaitChan)
+{
+ kmutex_t *mp;
+ sleepq_t *sq = sleeptab_lookup(&sleeptab, pvWaitChan, &mp);
+ sleepq_wake(sq, pvWaitChan, 1, mp);
+}
+
+/**
+ * Wakes up all waiters on the wait channel.
+ *
+ * @param pvWaitChan The opaque wait channel handle.
+ */
+DECLINLINE(void) rtR0SemBsdBroadcast(void *pvWaitChan)
+{
+ kmutex_t *mp;
+ sleepq_t *sq = sleeptab_lookup(&sleeptab, pvWaitChan, &mp);
+ sleepq_wake(sq, pvWaitChan, ~0u, mp);
+}
+
+/**
+ * Gets the max resolution of the timeout machinery.
+ *
+ * @returns Resolution specified in nanoseconds.
+ */
+DECLINLINE(uint32_t) rtR0SemBsdWaitGetResolution(void)
+{
+ return 1000000000 / hz; /* ns */
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_netbsd_sleepqueue_r0drv_netbsd_h */
diff --git a/src/VBox/Runtime/r0drv/netbsd/spinlock-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/spinlock-r0drv-netbsd.c
new file mode 100644
index 00000000..fe9c8d86
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/spinlock-r0drv-netbsd.c
@@ -0,0 +1,180 @@
+/* $Id: spinlock-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-netbsd-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/spinlock.h>
+#include <iprt/errcore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/thread.h>
+#include <iprt/mp.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the struct mtx type.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The spinlock. */
+ kmutex_t pSpinLock;
+ /** Saved interrupt flag. */
+ uint32_t volatile fIntSaved;
+ /** The spinlock creation flags. */
+ uint32_t fFlags;
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate.
+ */
+ AssertCompile(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize & return.
+ */
+ pThis->u32Magic = RTSPINLOCK_MAGIC;
+ pThis->fFlags = fFlags;
+ pThis->fIntSaved = 0;
+ if (fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE) {
+ mutex_init(&pThis->pSpinLock, MUTEX_DEFAULT, IPL_BIO);
+ } else {
+ mutex_init(&pThis->pSpinLock, MUTEX_DEFAULT, IPL_NONE);
+ }
+
+ *pSpinlock = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ RT_ASSERT_INTS_ON();
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertMsgReturn(pThis->u32Magic == RTSPINLOCK_MAGIC,
+ ("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Make the lock invalid and release the memory.
+ */
+ ASMAtomicIncU32(&pThis->u32Magic);
+ mutex_destroy(&pThis->pSpinLock);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE) {
+ mutex_spin_enter(&pThis->pSpinLock);
+ } else {
+ mutex_enter(&pThis->pSpinLock);
+ }
+}
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE) {
+ mutex_spin_exit(&pThis->pSpinLock);
+ } else {
+ mutex_exit(&pThis->pSpinLock);
+ }
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/the-netbsd-kernel.h b/src/VBox/Runtime/r0drv/netbsd/the-netbsd-kernel.h
new file mode 100644
index 00000000..ba493efd
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/the-netbsd-kernel.h
@@ -0,0 +1,107 @@
+/* $Id: the-netbsd-kernel.h $ */
+/** @file
+ * IPRT - Ring-0 Driver, The NetBSD Kernel Headers.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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.
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r0drv_netbsd_the_netbsd_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_netbsd_the_netbsd_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/* Deal with conflicts first. */
+#include <sys/param.h>
+#undef PVM
+#include <sys/bus.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/kernel.h>
+#include <sys/uio.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/syslimits.h>
+#include <sys/sleepq.h>
+#include <sys/unistd.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sched.h>
+#include <sys/callout.h>
+#include <sys/rwlock.h>
+#include <sys/kmem.h>
+#include <sys/cpu.h>
+#include <sys/vmmeter.h> /* cnt */
+#include <sys/resourcevar.h>
+#include <uvm/uvm.h>
+#include <uvm/uvm_extern.h>
+#include <uvm/uvm_page.h>
+#include <machine/cpu.h>
+
+/**
+ * Check whether we can use kmem_alloc_prot.
+ */
+#if 0 /** @todo Not available yet. */
+# define USE_KMEM_ALLOC_PROT
+#endif
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_netbsd_the_netbsd_kernel_h */
diff --git a/src/VBox/Runtime/r0drv/netbsd/thread-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/thread-r0drv-netbsd.c
new file mode 100644
index 00000000..842e1a75
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/thread-r0drv-netbsd.c
@@ -0,0 +1,193 @@
+/* $Id: thread-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Threads (Part 1), Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 "the-netbsd-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+#include "internal/thread.h"
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)curlwp;
+}
+
+
+static int rtR0ThreadNbsdSleepCommon(RTMSINTERVAL cMillies)
+{
+ int rc;
+ int cTicks;
+
+ /*
+ * 0 ms sleep -> yield.
+ */
+ if (!cMillies)
+ {
+ RTThreadYield();
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Translate milliseconds into ticks and go to sleep.
+ */
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ if (hz == 1000)
+ cTicks = cMillies;
+ else if (hz == 100)
+ cTicks = cMillies / 10;
+ else
+ {
+ int64_t cTicks64 = ((uint64_t)cMillies * hz) / 1000;
+ cTicks = (int)cTicks64;
+ if (cTicks != cTicks64)
+ cTicks = INT_MAX;
+ }
+ }
+ else
+ cTicks = 0; /* requires giant lock! */
+
+ rc = tsleep((void *)RTThreadSleep,
+ PZERO | PCATCH,
+ "iprtsl", /* max 6 chars */
+ cTicks);
+ switch (rc)
+ {
+ case 0:
+ return VINF_SUCCESS;
+ case EWOULDBLOCK:
+ return VERR_TIMEOUT;
+ case EINTR:
+ case ERESTART:
+ return VERR_INTERRUPTED;
+ default:
+ AssertMsgFailed(("%d\n", rc));
+ return VERR_NO_TRANSLATION;
+ }
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadNbsdSleepCommon(cMillies);
+}
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadNbsdSleepCommon(cMillies);
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ yield();
+ return true;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+
+ return curlwp->l_dopreempt == 0
+ && ASMIntAreEnabled(); /** @todo is there a native netbsd function/macro for this? */
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+
+ return curlwp->l_dopreempt;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* yes, RTThreadPreemptIsPending is reliable. */
+ return true;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+ /* yes, kernel preemption is possible. */
+ return true;
+}
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+
+ curlwp->l_nopreempt++;
+ __insn_barrier();
+}
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ __insn_barrier();
+ if (--curlwp->l_nopreempt != 0)
+ return;
+ __insn_barrier();
+ if (__predict_false(curlwp->l_dopreempt))
+ kpreempt(0);
+ __insn_barrier();
+}
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); NOREF(hThread);
+ /** @todo NetBSD: Implement RTThreadIsInInterrupt. Required for guest
+ * additions! */
+ return !ASMIntAreEnabled();
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/thread2-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/thread2-r0drv-netbsd.c
new file mode 100644
index 00000000..5ec65665
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/thread2-r0drv-netbsd.c
@@ -0,0 +1,167 @@
+/* $Id: thread2-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-netbsd-kernel.h"
+
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+#include "internal/thread.h"
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative(RTThreadNativeSelf());
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ int iPriority;
+
+ switch (enmType)
+ {
+ case RTTHREADTYPE_INFREQUENT_POLLER: iPriority = PZERO + 8; break;
+ case RTTHREADTYPE_EMULATION: iPriority = PZERO + 4; break;
+ case RTTHREADTYPE_DEFAULT: iPriority = PZERO; break;
+ case RTTHREADTYPE_MSG_PUMP: iPriority = PZERO - 4; break;
+ case RTTHREADTYPE_IO: iPriority = PRIBIO; break;
+ case RTTHREADTYPE_TIMER: iPriority = PSWP; break;
+ default:
+ AssertMsgFailed(("enmType=%d\n", enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ lwp_lock(curlwp);
+ lwp_changepri(curlwp, iPriority);
+ lwp_unlock(curlwp);
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+ /* There is nothing special that needs doing here, but the
+ user really better know what he's cooking. */
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ /** @todo fix RTThreadWait/RTR0Term race on netbsd. */
+ RTThreadSleep(1);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+/**
+ * Native thread main function.
+ *
+ * @param pvThreadInt The thread structure.
+ */
+static void rtThreadNativeMain(void *pvThreadInt)
+{
+ const struct lwp *Self = curlwp;
+ PRTTHREADINT pThreadInt = (PRTTHREADINT)pvThreadInt;
+ int rc;
+
+ rc = rtThreadMain(pThreadInt, (RTNATIVETHREAD)Self, &pThreadInt->szName[0]);
+
+ kthread_exit(rc);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+ int rc;
+ struct lwp *l;
+
+ rc = kthread_create(PRI_NONE, 0, NULL, rtThreadNativeMain, (void *)pThreadInt, &l, "%s", pThreadInt->szName);
+
+ if (!rc)
+ {
+ *pNativeThread = (RTNATIVETHREAD)l;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromErrno(rc);
+ return rc;
+}
diff --git a/src/VBox/Runtime/r0drv/netbsd/time-r0drv-netbsd.c b/src/VBox/Runtime/r0drv/netbsd/time-r0drv-netbsd.c
new file mode 100644
index 00000000..a65ca257
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/netbsd/time-r0drv-netbsd.c
@@ -0,0 +1,105 @@
+/* $Id: time-r0drv-netbsd.c $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, NetBSD.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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
+ * --------------------------------------------------------------------
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-netbsd-kernel.h"
+#define RTTIME_INCL_TIMESPEC
+
+#include <iprt/time.h>
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ struct timespec tsp;
+ nanotime(&tsp);
+ return tsp.tv_sec * RT_NS_1SEC_64
+ + tsp.tv_nsec;
+}
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return RTTimeNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return RTTimeNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return RTTimeMilliTS();
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ struct timespec tsp;
+ nanotime(&tsp);
+ return RTTimeSpecSetTimespec(pTime, &tsp);
+}
diff --git a/src/VBox/Runtime/r0drv/nt/Makefile.kup b/src/VBox/Runtime/r0drv/nt/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp
new file mode 100644
index 00000000..8ba622c6
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/RTLogWriteDebugger-r0drv-nt.cpp
@@ -0,0 +1,49 @@
+/* $Id: RTLogWriteDebugger-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Log To Debugger, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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-nt-kernel.h"
+#include <iprt/log.h>
+#include <iprt/assert.h>
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ if (pch[cb] != '\0')
+ AssertBreakpoint();
+ DbgPrint("%s", pch);
+ return;
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp
new file mode 100644
index 00000000..7afcc823
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/RTTimerGetSystemGranularity-r0drv-nt.cpp
@@ -0,0 +1,71 @@
+/* $Id: RTTimerGetSystemGranularity-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - RTTimerGetSystemGranularity, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+
+#include <iprt/timer.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+
+#include "internal-r0drv-nt.h"
+#include "internal/magics.h"
+
+
+RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
+{
+ /*
+ * Get the default/max timer increment value, return it if ExSetTimerResolution
+ * isn't available. According to the sysinternals guys NtQueryTimerResolution
+ * is only available in userland and they find it equally annoying.
+ */
+ ULONG ulTimeInc = KeQueryTimeIncrement();
+ if (!g_pfnrtNtExSetTimerResolution)
+ return ulTimeInc * 100; /* The value is in 100ns, the funny NT unit. */
+
+ /*
+ * Use the value returned by ExSetTimerResolution. Since the kernel is keeping
+ * count of these calls, we have to do two calls that cancel each other out.
+ */
+ g_pfnrtNtExSetTimerResolution(ulTimeInc, TRUE);
+ ULONG ulResolution = g_pfnrtNtExSetTimerResolution(0 /*ignored*/, FALSE);
+ return ulResolution * 100; /* NT -> ns */
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp
new file mode 100644
index 00000000..5d9ae530
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/alloc-r0drv-nt.cpp
@@ -0,0 +1,145 @@
+/* $Id: alloc-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "r0drv/alloc-r0drv.h"
+#include "internal-r0drv-nt.h"
+
+
+/**
+ * OS specific allocation function.
+ */
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ if (!(fFlags & RTMEMHDR_FLAG_ANY_CTX))
+ {
+ PRTMEMHDR pHdr;
+ POOL_TYPE const enmPoolType = g_uRtNtVersion >= RTNT_MAKE_VERSION(8,0) ? NonPagedPoolNx : NonPagedPool;
+ if (g_pfnrtExAllocatePoolWithTag)
+ pHdr = (PRTMEMHDR)g_pfnrtExAllocatePoolWithTag(enmPoolType, cb + sizeof(*pHdr), IPRT_NT_POOL_TAG);
+ else
+ {
+ fFlags |= RTMEMHDR_FLAG_UNTAGGED;
+ pHdr = (PRTMEMHDR)ExAllocatePool(enmPoolType, cb + sizeof(*pHdr));
+ }
+ if (RT_LIKELY(pHdr))
+ {
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = (uint32_t)cb; Assert(pHdr->cb == cb);
+ pHdr->cbReq = (uint32_t)cb;
+ *ppHdr = pHdr;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * OS specific free function.
+ */
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr)
+{
+ pHdr->u32Magic = RTMEMHDR_MAGIC_DEAD;
+ if (g_pfnrtExFreePoolWithTag && !(pHdr->fFlags & RTMEMHDR_FLAG_UNTAGGED))
+ g_pfnrtExFreePoolWithTag(pHdr, IPRT_NT_POOL_TAG);
+ else
+ ExFreePool(pHdr);
+}
+
+
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ /*
+ * validate input.
+ */
+ AssertPtr(pPhys);
+ Assert(cb > 0);
+
+ /*
+ * Allocate and get physical address.
+ * Make sure the return is page aligned.
+ */
+ PHYSICAL_ADDRESS MaxPhysAddr;
+ MaxPhysAddr.HighPart = 0;
+ MaxPhysAddr.LowPart = 0xffffffff;
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE);
+ void *pv = MmAllocateContiguousMemory(cb, MaxPhysAddr);
+ if (pv)
+ {
+ if (!((uintptr_t)pv & PAGE_OFFSET_MASK)) /* paranoia */
+ {
+ PHYSICAL_ADDRESS PhysAddr = MmGetPhysicalAddress(pv);
+ if (!PhysAddr.HighPart) /* paranoia */
+ {
+ *pPhys = (RTCCPHYS)PhysAddr.LowPart;
+ return pv;
+ }
+
+ /* failure */
+ AssertMsgFailed(("MMAllocContiguousMemory returned high address! PhysAddr=%RX64\n", (uint64_t)PhysAddr.QuadPart));
+ }
+ else
+ AssertMsgFailed(("MMAllocContiguousMemory didn't return a page aligned address - %p!\n", pv));
+
+ MmFreeContiguousMemory(pv);
+ }
+
+ return NULL;
+}
+
+
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
+{
+ if (pv)
+ {
+ Assert(cb > 0); NOREF(cb);
+ AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv));
+ MmFreeContiguousMemory(pv);
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm b/src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm
new file mode 100644
index 00000000..71d99e9d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/alloca-x86-r0drv-nt.asm
@@ -0,0 +1,69 @@
+; $Id: alloca-x86-r0drv-nt.asm $
+;; @file
+; IPRT - Visual C++ __alloca__probe_16.
+;
+
+;
+; Copyright (C) 2020-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
+
+;;
+; @returns esp with eax subtracted and aligned to 16 bytes.
+; @param eax allocation size.
+;
+BEGINPROC _alloca_probe_16
+ ; Sanitycheck the input.
+%ifdef DEBUG
+ cmp eax, 0
+ jne .not_zero
+ int3
+.not_zero:
+ cmp eax, 4096
+ jbe .four_kb_or_less
+ int3
+.four_kb_or_less:
+%endif
+
+ ; Don't bother probing the stack as the allocation is supposed to be
+ ; a lot smaller than 4KB.
+ neg eax
+ lea eax, [esp + eax + 4]
+ and eax, 0fffffff0h
+ xchg eax, esp
+ jmp [eax]
+ENDPROC _alloca_probe_16
+
diff --git a/src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp
new file mode 100644
index 00000000..cade19b1
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/assert-r0drv-nt.cpp
@@ -0,0 +1,76 @@
+/* $Id: assert-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+
+#include "internal/assert.h"
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ DbgPrint("\n!!Assertion Failed!!\n"
+ "Expression: %s\n"
+ "Location : %s(%d) %s\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+ char szMsg[256];
+
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+ DbgPrint("%s", szMsg);
+
+ NOREF(fInitial);
+}
+
+
+RTR0DECL(void) RTR0AssertPanicSystem(void)
+{
+ /** @todo implement RTR0AssertPanicSystem. */
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp
new file mode 100644
index 00000000..90e7ef6a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/dbgkrnlinfo-r0drv-nt.cpp
@@ -0,0 +1,918 @@
+/* $Id: dbgkrnlinfo-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Kernel Debug Information, R0 Driver, NT.
+ */
+
+/*
+ * 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 IMAGE_NT_HEADERS NT_IMAGE_NT_HEADERS
+#define IMAGE_NT_HEADERS32 NT_IMAGE_NT_HEADERS32
+#define IMAGE_NT_HEADERS64 NT_IMAGE_NT_HEADERS64
+#define PIMAGE_NT_HEADERS NT_PIMAGE_NT_HEADERS
+#define PIMAGE_NT_HEADERS32 NT_PIMAGE_NT_HEADERS32
+#define PIMAGE_NT_HEADERS64 NT_PIMAGE_NT_HEADERS64
+#ifndef IPRT_NT_MAP_TO_ZW
+# define IPRT_NT_MAP_TO_ZW
+#endif
+#include "the-nt-kernel.h"
+#include <iprt/dbg.h>
+
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal-r0drv-nt.h"
+#include "internal/magics.h"
+
+#undef IMAGE_NT_HEADERS
+#undef IMAGE_NT_HEADERS32
+#undef IMAGE_NT_HEADERS64
+#undef PIMAGE_NT_HEADERS
+#undef PIMAGE_NT_HEADERS32
+#undef PIMAGE_NT_HEADERS64
+#include <iprt/formats/pecoff.h>
+#include <iprt/formats/mz.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Private logging macro, will use DbgPrint! */
+#ifdef IN_GUEST
+# define RTR0DBG_NT_ERROR_LOG(a) do { RTLogBackdoorPrintf a; DbgPrint a; } while (0)
+# define RTR0DBG_NT_DEBUG_LOG(a) do { RTLogBackdoorPrintf a; DbgPrint a; } while (0)
+#else
+# define RTR0DBG_NT_ERROR_LOG(a) do { DbgPrint a; } while (0)
+# define RTR0DBG_NT_DEBUG_LOG(a) do { DbgPrint a; } while (0)
+#endif
+#ifndef LOG_ENABLED
+# undef RTR0DBG_NT_DEBUG_LOG
+# define RTR0DBG_NT_DEBUG_LOG(a) do { } while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#define PIMAGE_NT_HEADERS RT_CONCAT(PIMAGE_NT_HEADERS, ARCH_BITS)
+
+/**
+ * Information we cache for a kernel module.
+ */
+typedef struct RTDBGNTKRNLMODINFO
+{
+ /** The module name. */
+ char szName[32];
+
+ /** The image base. */
+ uint8_t const *pbImageBase;
+ /** The NT headers. */
+ PIMAGE_NT_HEADERS pNtHdrs;
+ /** Set if this module parsed okay and all fields are valid. */
+ bool fOkay;
+ /** 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;
+} RTDBGNTKRNLMODINFO;
+/** Pointer to kernel module info. */
+typedef RTDBGNTKRNLMODINFO *PRTDBGNTKRNLMODINFO;
+/** Pointer to const kernel module info. */
+typedef RTDBGNTKRNLMODINFO const *PCRTDBGNTKRNLMODINFO;
+
+
+/**
+ * NT kernel info instance.
+ */
+typedef struct RTDBGKRNLINFOINT
+{
+ /** Magic value (RTDBGKRNLINFO_MAGIC). */
+ uint32_t u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Number of additional modules in the cache. */
+ uint32_t cModules;
+ /** Additional modules. */
+ RTDBGNTKRNLMODINFO aModules[3];
+} RTDBGKRNLINFOINT;
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to MmGetSystemRoutineAddress.
+ * @note Added in NT v5.0. */
+static decltype(MmGetSystemRoutineAddress) *g_pfnMmGetSystemRoutineAddress = NULL;
+/** Info about the ntoskrnl.exe mapping. */
+static RTDBGNTKRNLMODINFO g_NtOsKrnlInfo = { "ntoskrnl.exe", NULL, NULL, false, 0, 0, 0, 0, 0, NULL, 0, 0, NULL, NULL };
+/** Info about the hal.dll mapping. */
+static RTDBGNTKRNLMODINFO g_HalInfo = { "hal.dll", NULL, NULL, false, 0, 0, 0, 0, 0, NULL, 0, 0, NULL, NULL };
+
+
+
+/**
+ * Looks up an symbol int the export table.
+ *
+ * @returns VINF_SUCCESS or VERR_SYMBOL_NOT_FOUND.
+ * @param pModInfo The module info.
+ * @param pszSymbol The symbol to find.
+ * @param cForwarders Forwarder nesting depth.
+ * @param ppvSymbol Where to put the symbol address.
+ *
+ * @note Support library has similar code for in the importless area.
+ */
+static int rtR0DbgKrnlInfoLookupSymbol(PCRTDBGNTKRNLMODINFO pModInfo, const char *pszSymbol, unsigned cForwarders,
+ void **ppvSymbol)
+{
+ if (pModInfo->fOkay)
+ {
+ /*
+ * Pseudo symbols:
+ */
+ if ( pszSymbol[0] == '_'
+ && pszSymbol[1] == '_'
+ && pszSymbol[2] == 'I')
+ {
+ if (strcmp(pszSymbol, "__ImageBase") == 0)
+ {
+ *ppvSymbol = (void *)pModInfo->pbImageBase;
+ return VINF_SUCCESS;
+ }
+ if (strcmp(pszSymbol, "__ImageSize") == 0)
+ {
+ *ppvSymbol = (void *)(uintptr_t)pModInfo->cbImage;
+ return VINF_SUCCESS;
+ }
+ if (strcmp(pszSymbol, "__ImageNtHdrs") == 0)
+ {
+ *ppvSymbol = pModInfo->pNtHdrs;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Binary search.
+ */
+ __try
+ {
+ uint32_t iStart = 0;
+ uint32_t iEnd = pModInfo->cNamedExports;
+ while (iStart < iEnd)
+ {
+ uint32_t iCur = iStart + (iEnd - iStart) / 2;
+ uint32_t offExpName = pModInfo->paoffNamedExports[iCur];
+ if (offExpName >= pModInfo->offEndSectHdrs && offExpName < pModInfo->cbImage)
+ { /* likely */ }
+ else
+ {
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: %s: Bad export name entry: %#x (iCur=%#x)\n",
+ pModInfo->szName, offExpName, iCur));
+ break;
+ }
+
+ const char *pszExpName = (const char *)&pModInfo->pbImageBase[offExpName];
+ int iDiff = strcmp(pszExpName, pszSymbol);
+ 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 = pModInfo->pau16NameOrdinals[iCur];
+ if (iExpOrdinal < pModInfo->cExports)
+ {
+ uint32_t offExport = pModInfo->paoffExports[iExpOrdinal];
+ if (offExport - pModInfo->offExportDir >= pModInfo->cbExportDir)
+ {
+ *ppvSymbol = (void *)&pModInfo->pbImageBase[offExport];
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Deal with forwarders to NT and HAL. No ordinals.
+ */
+ const char *pszForwarder = (const char *)&pModInfo->pbImageBase[offExport];
+ uint32_t cbMax = pModInfo->cbImage - offExpName;
+ size_t cchForwarder = RTStrNLen(pszForwarder, cbMax);
+ if (cchForwarder < cbMax)
+ {
+ if ( cchForwarder > 9
+ && pModInfo != &g_NtOsKrnlInfo
+ && g_NtOsKrnlInfo.pbImageBase != NULL
+ && cForwarders < 2
+ && (pszForwarder[0] == 'n' || pszForwarder[0] == 'N')
+ && (pszForwarder[1] == 't' || pszForwarder[1] == 'T')
+ && (pszForwarder[2] == 'o' || pszForwarder[2] == 'O')
+ && (pszForwarder[3] == 's' || pszForwarder[3] == 'S')
+ && (pszForwarder[4] == 'k' || pszForwarder[4] == 'K')
+ && (pszForwarder[5] == 'r' || pszForwarder[5] == 'R')
+ && (pszForwarder[6] == 'n' || pszForwarder[6] == 'N')
+ && (pszForwarder[7] == 'l' || pszForwarder[7] == 'L')
+ && pszForwarder[8] == '.')
+ return rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, pszForwarder + 9, cForwarders + 1, ppvSymbol);
+
+ if ( cchForwarder > 4
+ && pModInfo != &g_HalInfo
+ && g_HalInfo.pbImageBase != NULL
+ && cForwarders < 2
+ && (pszForwarder[0] == 'h' || pszForwarder[0] == 'H')
+ && (pszForwarder[1] == 'a' || pszForwarder[1] == 'A')
+ && (pszForwarder[2] == 'l' || pszForwarder[2] == 'L')
+ && pszForwarder[3] == '.')
+ return rtR0DbgKrnlInfoLookupSymbol(&g_HalInfo, pszForwarder + 4, cForwarders + 1, ppvSymbol);
+ }
+
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: %s: Forwarded symbol '%s': offExport=%#x (dir %#x LB %#x)\n",
+ pModInfo->szName, pszSymbol, offExport, pModInfo->offExportDir, pModInfo->cbExportDir));
+ }
+ else
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: %s: Name ordinal for '%s' is out of bounds: %#x (max %#x)\n",
+ pModInfo->szName, iExpOrdinal, pModInfo->cExports));
+ break;
+ }
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlInfoLookupSymbol: Exception searching '%s' for '%s'...\n",
+ pModInfo->szName, pszSymbol));
+ }
+ }
+
+ *ppvSymbol = NULL;
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+/**
+ * Parses (PE) module headers and fills in the coresponding module info struct.
+ *
+ * @returns true on if success, false if not.
+ * @param pModInfo The module info structure to fill in with parsed
+ * data. The szName and fOkay are set by the
+ * caller, this function does the rest.
+ * @param pbMapping The image mapping address
+ * @param cbMapping The image mapping size.
+ *
+ * @note Support library has similar code for in the importless area.
+ */
+static bool rtR0DbgKrnlNtParseModule(PRTDBGNTKRNLMODINFO pModInfo, uint8_t const *pbMapping, size_t cbMapping)
+{
+#define MODERR_RETURN(a_LogMsg, ...) \
+ do { RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtParseModule: " a_LogMsg, __VA_ARGS__)); return false; } while (0)
+
+ pModInfo->pbImageBase = pbMapping;
+
+ /*
+ * Locate the PE header, do some basic validations.
+ */
+ IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbMapping;
+ uint32_t offNtHdrs = 0;
+ PIMAGE_NT_HEADERS pNtHdrs;
+ if (pMzHdr->e_magic == IMAGE_DOS_SIGNATURE)
+ {
+ offNtHdrs = pMzHdr->e_lfanew;
+ if (offNtHdrs > _2K)
+ MODERR_RETURN("%s: e_lfanew=%#x, expected a lower value\n", pModInfo->szName, offNtHdrs);
+ }
+ pModInfo->pNtHdrs = pNtHdrs = (PIMAGE_NT_HEADERS)&pbMapping[offNtHdrs];
+
+ if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE)
+ MODERR_RETURN("%s: Invalid PE signature: %#x", pModInfo->szName, pNtHdrs->Signature);
+ if (pNtHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pNtHdrs->OptionalHeader))
+ MODERR_RETURN("%s: Unexpected optional header size: %#x\n", pModInfo->szName, pNtHdrs->FileHeader.SizeOfOptionalHeader);
+ if (pNtHdrs->OptionalHeader.Magic != RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
+ MODERR_RETURN("%s: Unexpected optional header magic: %#x\n", pModInfo->szName, pNtHdrs->OptionalHeader.Magic);
+ if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
+ MODERR_RETURN("%s: Unexpected number of RVA and sizes: %#x\n", pModInfo->szName, pNtHdrs->OptionalHeader.NumberOfRvaAndSizes);
+
+ pModInfo->offNtHdrs = offNtHdrs;
+ pModInfo->offEndSectHdrs = offNtHdrs
+ + sizeof(*pNtHdrs)
+ + pNtHdrs->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ pModInfo->cbImage = pNtHdrs->OptionalHeader.SizeOfImage;
+ if (pModInfo->cbImage > cbMapping)
+ MODERR_RETURN("%s: The image size %#x is larger than the mapping: %#x\n",
+ pModInfo->szName, pModInfo->cbImage, cbMapping);
+
+ /*
+ * Find the export directory. It's okay if none is present too.
+ */
+ IMAGE_DATA_DIRECTORY ExpDir = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ if ( ExpDir.Size < sizeof(IMAGE_EXPORT_DIRECTORY)
+ || ExpDir.VirtualAddress < pModInfo->offEndSectHdrs
+ || ExpDir.VirtualAddress >= pModInfo->cbImage
+ || ExpDir.VirtualAddress + ExpDir.Size > pModInfo->cbImage)
+ {
+ if (ExpDir.Size == 0 && ExpDir.VirtualAddress == 0)
+ {
+ pModInfo->offExportDir = 0;
+ pModInfo->cbExportDir = 0;
+ pModInfo->cNamedExports = 0;
+ pModInfo->cExports = 0;
+ pModInfo->paoffExports = NULL;
+ pModInfo->paoffNamedExports = NULL;
+ pModInfo->pau16NameOrdinals = NULL;
+ return true;
+ }
+ MODERR_RETURN("%s: Missing or invalid export directory: %#lx LB %#x\n", pModInfo->szName, ExpDir.VirtualAddress, ExpDir.Size);
+ }
+ pModInfo->offExportDir = ExpDir.VirtualAddress;
+ pModInfo->cbExportDir = ExpDir.Size;
+
+ IMAGE_EXPORT_DIRECTORY const *pExpDir = (IMAGE_EXPORT_DIRECTORY const *)&pbMapping[ExpDir.VirtualAddress];
+
+ if ( pExpDir->NumberOfFunctions >= _1M
+ || pExpDir->NumberOfFunctions < 1
+ || pExpDir->NumberOfNames >= _1M
+ || pExpDir->NumberOfNames < 1)
+ MODERR_RETURN("%s: NumberOfNames or/and NumberOfFunctions are outside the expected range: nof=%#x non=%#x\n",
+ pModInfo->szName, pExpDir->NumberOfFunctions, pExpDir->NumberOfNames);
+ pModInfo->cNamedExports = pExpDir->NumberOfNames;
+ pModInfo->cExports = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
+
+ if ( pExpDir->AddressOfFunctions < pModInfo->offEndSectHdrs
+ || pExpDir->AddressOfFunctions >= pModInfo->cbImage
+ || pExpDir->AddressOfFunctions + pModInfo->cExports * sizeof(uint32_t) > pModInfo->cbImage)
+ MODERR_RETURN("%s: Bad AddressOfFunctions: %#x\n", pModInfo->szName, pExpDir->AddressOfFunctions);
+ pModInfo->paoffExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfFunctions];
+
+ if ( pExpDir->AddressOfNames < pModInfo->offEndSectHdrs
+ || pExpDir->AddressOfNames >= pModInfo->cbImage
+ || pExpDir->AddressOfNames + pExpDir->NumberOfNames * sizeof(uint32_t) > pModInfo->cbImage)
+ MODERR_RETURN("%s: Bad AddressOfNames: %#x\n", pModInfo->szName, pExpDir->AddressOfNames);
+ pModInfo->paoffNamedExports = (uint32_t const *)&pbMapping[pExpDir->AddressOfNames];
+
+ if ( pExpDir->AddressOfNameOrdinals < pModInfo->offEndSectHdrs
+ || pExpDir->AddressOfNameOrdinals >= pModInfo->cbImage
+ || pExpDir->AddressOfNameOrdinals + pExpDir->NumberOfNames * sizeof(uint32_t) > pModInfo->cbImage)
+ MODERR_RETURN("%s: Bad AddressOfNameOrdinals: %#x\n", pModInfo->szName, pExpDir->AddressOfNameOrdinals);
+ pModInfo->pau16NameOrdinals = (uint16_t const *)&pbMapping[pExpDir->AddressOfNameOrdinals];
+
+ /*
+ * Success.
+ */
+ return true;
+#undef MODERR_RETURN
+}
+
+
+/**
+ * Searches the given module information from the kernel for the NT kernel module, the
+ * HAL module, and optionally one more module.
+ *
+ * If the NT kernel or HAL modules have already been found, they'll be skipped.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_LDR_GENERAL_FAILURE if we failed to parse the NT kernel or HAL.
+ * @retval VERR_BAD_EXE_FORMAT if we failed to parse @a pModInfo.
+ * @retval VERR_MODULE_NOT_FOUND if @a pModInfo wasn't found.
+ *
+ * @param pInfo Pointer to the module information.
+ * @param cModules Number of valid module entries in the module information pointer.
+ * @param pModInfo Custom module to search for. Optional.
+ */
+static int rtR0DbgKrnlNtSearchForModuleWorker(PRTL_PROCESS_MODULES pInfo, uint32_t cModules, PRTDBGNTKRNLMODINFO pModInfo)
+{
+ AssertPtrReturn(pInfo, VERR_INVALID_PARAMETER);
+ AssertReturn(cModules >= 2, VERR_INVALID_PARAMETER);
+
+ /*
+ * Search the info. The information is ordered with the kernel bits first,
+ * we expect aleast two modules to be returned to us (kernel + hal)!
+ */
+ int rc = VINF_SUCCESS;
+#if ARCH_BITS == 32
+ uintptr_t const uMinKernelAddr = _2G; /** @todo resolve MmSystemRangeStart */
+#else
+ uintptr_t const uMinKernelAddr = (uintptr_t)MM_SYSTEM_RANGE_START;
+#endif
+
+ for (uint32_t iModule = 0; iModule < cModules; iModule++)
+ RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: [%u]= %p LB %#x %s\n", iModule, pInfo->Modules[iModule].ImageBase,
+ pInfo->Modules[iModule].ImageSize, pInfo->Modules[iModule].FullPathName));
+
+ /*
+ * First time around we serch for the NT kernel and HAL. We'll look for NT
+ * kerneland HAL in the first 16 entries, and if not found, use the first
+ * and second entry respectively.
+ */
+ if ( !g_NtOsKrnlInfo.pbImageBase
+ && !g_HalInfo.pbImageBase)
+ {
+ /* Find them. */
+ RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Looking for kernel and hal...\n"));
+ uint32_t const cMaxModules = RT_MIN(cModules, 16);
+ uint32_t idxNtOsKrnl = UINT32_MAX;
+ uint32_t idxHal = UINT32_MAX;
+ for (uint32_t iModule = 0; iModule < cMaxModules; iModule++)
+ {
+ RTL_PROCESS_MODULE_INFORMATION const * const pModule = &pInfo->Modules[iModule];
+ if ( (uintptr_t)pModule->ImageBase >= uMinKernelAddr
+ && (uintptr_t)pModule->ImageSize >= _4K)
+ {
+ const char *pszName = (const char *)&pModule->FullPathName[pModule->OffsetToFileName];
+ if ( idxNtOsKrnl == UINT32_MAX
+ && RTStrICmpAscii(pszName, g_NtOsKrnlInfo.szName) == 0)
+ {
+ idxNtOsKrnl = iModule;
+ if (idxHal != UINT32_MAX)
+ break;
+ }
+ else if ( idxHal == UINT32_MAX
+ && RTStrICmpAscii(pszName, g_HalInfo.szName) == 0)
+ {
+ idxHal = iModule;
+ if (idxHal != UINT32_MAX)
+ break;
+ }
+ }
+ }
+ RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: idxNtOsKrnl=%#x idxHal=%#x\n", idxNtOsKrnl, idxHal));
+ if (idxNtOsKrnl == UINT32_MAX)
+ {
+ idxNtOsKrnl = 0;
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: 'ntoskrnl.exe' not found, picking '%s' instead\n",
+ pInfo->Modules[idxNtOsKrnl].FullPathName));
+ }
+ if (idxHal == UINT32_MAX)
+ {
+ idxHal = 1;
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: 'hal.dll' not found, picking '%s' instead\n",
+ pInfo->Modules[idxHal].FullPathName));
+ }
+
+ /* Parse them. */
+ //RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Parsing NT kernel...\n"));
+ __try
+ {
+ g_NtOsKrnlInfo.fOkay = rtR0DbgKrnlNtParseModule(&g_NtOsKrnlInfo,
+ (uint8_t const *)pInfo->Modules[idxNtOsKrnl].ImageBase,
+ pInfo->Modules[idxNtOsKrnl].ImageSize);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ g_NtOsKrnlInfo.fOkay = false;
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: Exception in rtR0DbgKrnlNtParseModule parsing ntoskrnl.exe...\n"));
+ }
+
+ //RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Parsing HAL...\n"));
+ __try
+ {
+ g_HalInfo.fOkay = rtR0DbgKrnlNtParseModule(&g_HalInfo, (uint8_t const *)pInfo->Modules[idxHal].ImageBase,
+ pInfo->Modules[idxHal].ImageSize);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ g_HalInfo.fOkay = false;
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: Exception in rtR0DbgKrnlNtParseModule parsing hal.dll...\n"));
+ }
+ if (!g_NtOsKrnlInfo.fOkay || !g_HalInfo.fOkay)
+ rc = VERR_LDR_GENERAL_FAILURE;
+
+ /*
+ * Resolve symbols we may need in the NT kernel (provided it parsed successfully)
+ */
+ if (g_NtOsKrnlInfo.fOkay)
+ {
+ if (!g_pfnMmGetSystemRoutineAddress)
+ {
+ //RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Looking up 'MmGetSystemRoutineAddress'...\n"));
+ rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, "MmGetSystemRoutineAddress", 0,
+ (void **)&g_pfnMmGetSystemRoutineAddress);
+ }
+ }
+ }
+
+ /*
+ * If we're still good, search for the given module (optional).
+ */
+ if (RT_SUCCESS(rc) && pModInfo)
+ {
+ RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: Locating module '%s'...\n", pModInfo->szName));
+ rc = VERR_MODULE_NOT_FOUND;
+ for (uint32_t iModule = 0; iModule < cModules; iModule++)
+ {
+ RTL_PROCESS_MODULE_INFORMATION const * const pModule = &pInfo->Modules[iModule];
+ if ( (uintptr_t)pModule->ImageBase >= uMinKernelAddr
+ && (uintptr_t)pModule->ImageSize >= _4K)
+ {
+ const char *pszName = (const char *)&pModule->FullPathName[pModule->OffsetToFileName];
+ if ( pModInfo->pbImageBase == NULL
+ && RTStrICmpAscii(pszName, pModInfo->szName) == 0)
+ {
+ /*
+ * Found the module, try parse it.
+ */
+ __try
+ {
+ pModInfo->fOkay = rtR0DbgKrnlNtParseModule(pModInfo, (uint8_t const *)pModule->ImageBase,
+ pModule->ImageSize);
+ rc = VINF_SUCCESS;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ pModInfo->fOkay = false;
+ rc = VERR_BAD_EXE_FORMAT;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Queries the given maximum amount of modules and returns a pointer to the
+ * allocation holding the modules.
+ *
+ * @returns IPRT status code.
+ * @param ppInfo Where to store the pointer to the module information structure on success.
+ * Free with RTMemFree() when done.
+ * @param cModulesMax Maximum number of modules to return.
+ * @param pcModules Where to store the amount of modules returned upon success,
+ * can be lower than the requested maximum.
+ */
+static int rtR0DbgKrnlNtQueryModules(PRTL_PROCESS_MODULES *ppInfo, uint32_t cModulesMax, uint32_t *pcModules)
+{
+ *ppInfo = NULL;
+ *pcModules = 0;
+
+ ULONG cbInfo = RT_UOFFSETOF_DYN(RTL_PROCESS_MODULES, Modules[cModulesMax]);
+ PRTL_PROCESS_MODULES pInfo = (PRTL_PROCESS_MODULES)RTMemAllocZ(cbInfo);
+ if (!pInfo)
+ {
+ cModulesMax = cModulesMax / 4;
+ cbInfo = RT_UOFFSETOF_DYN(RTL_PROCESS_MODULES, Modules[cModulesMax]);
+ pInfo = (PRTL_PROCESS_MODULES)RTMemAllocZ(cbInfo);
+ if (!pInfo)
+ {
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtQueryModules: Out of memory!\n"));
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ int rc;
+ ULONG cbActual = 0;
+ NTSTATUS rcNt = ZwQuerySystemInformation(SystemModuleInformation, pInfo, cbInfo, &cbActual);
+ RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtQueryModules: ZwQuerySystemInformation returned %#x and NumberOfModules=%#x\n",
+ rcNt, pInfo->NumberOfModules));
+ if ( NT_SUCCESS(rcNt)
+ || rcNt == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ *ppInfo = pInfo;
+ *pcModules = RT_MIN(cModulesMax, pInfo->NumberOfModules);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ RTMemFree(pInfo);
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtQueryModules: ZwQuerySystemInformation failed: %#x\n", rcNt));
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Searches the module information from the kernel for the NT kernel module, the
+ * HAL module, and optionally one more module.
+ *
+ * If the NT kernel or HAL modules have already been found, they'll be skipped.
+ *
+ * @returns IPRT status code.
+ * @retval VERR_LDR_GENERAL_FAILURE if we failed to parse the NT kernel or HAL.
+ * @retval VERR_BAD_EXE_FORMAT if we failed to parse @a pModInfo.
+ * @retval VERR_MODULE_NOT_FOUND if @a pModInfo wasn't found.
+ * @retval VERR_BUFFER_UNDERFLOW if less that two modules was returned by the
+ * system.
+ *
+ * @param pModInfo Custom module to search for. Optional.
+ */
+static int rtR0DbgKrnlNtInit(PRTDBGNTKRNLMODINFO pModInfo)
+{
+ RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: pModInfo=%p\n", pModInfo));
+
+#ifndef IPRT_TARGET_NT4
+ /*
+ * Must manually initialize g_pfnMmGetSystemRoutineAddress, otherwise compiler
+ * generates its own dynamic init code that might not necessarily be called.
+ */
+ g_pfnMmGetSystemRoutineAddress = MmGetSystemRoutineAddress;
+#endif
+
+ /*
+ * Allocate a reasonably large buffer and get the information we need. We don't
+ * need everything since the result starts off with the kernel bits in load order.
+ *
+ * Note! ZwQuerySystemInformation requires NT4. For 3.51 we could possibly emit
+ * the syscall ourselves, if we cared.
+ */
+ uint32_t cModules = 0;
+ PRTL_PROCESS_MODULES pInfo = NULL;
+ int rc = rtR0DbgKrnlNtQueryModules(&pInfo, pModInfo ? 110 /*32KB*/ : 27 /*8KB*/, &cModules);
+ if (RT_SUCCESS(rc))
+ {
+ if (cModules >= 2)
+ {
+ rc = rtR0DbgKrnlNtSearchForModuleWorker(pInfo, cModules, pModInfo);
+ if ( rc == VERR_MODULE_NOT_FOUND
+ && pInfo->NumberOfModules > cModules
+ && pModInfo)
+ {
+ /* Module not found in the first round, reallocate array to maximum size and rerun. */
+ cModules = pInfo->NumberOfModules;
+
+ RTMemFree(pInfo);
+ pInfo = NULL;
+
+ rc = rtR0DbgKrnlNtQueryModules(&pInfo, cModules, &cModules);
+ if (RT_SUCCESS(rc))
+ rc = rtR0DbgKrnlNtSearchForModuleWorker(pInfo, cModules, pModInfo);
+ }
+ }
+ else
+ {
+ RTR0DBG_NT_ERROR_LOG(("rtR0DbgKrnlNtInit: Error! Only %u module(s) returned!\n", cModules));
+ rc = VERR_BUFFER_UNDERFLOW;
+ }
+
+ RTMemFree(pInfo);
+ }
+
+ RTR0DBG_NT_DEBUG_LOG(("rtR0DbgKrnlNtInit: returns %d\n", rc));
+ return rc;
+}
+
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoOpen(PRTDBGKRNLINFO phKrnlInfo, uint32_t fFlags)
+{
+ AssertReturn(!fFlags, VERR_INVALID_FLAGS);
+
+ RTDBGKRNLINFOINT *pThis = (RTDBGKRNLINFOINT *)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTDBGKRNLINFO_MAGIC;
+ pThis->cRefs = 1;
+ *phKrnlInfo = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTR0DECL(uint32_t) RTR0DbgKrnlInfoRetain(RTDBGKRNLINFO hKrnlInfo)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+ return cRefs;
+}
+
+
+static void rtR0DbgKrnlNtDtor(RTDBGKRNLINFOINT *pThis)
+{
+ pThis->u32Magic = ~RTDBGKRNLINFO_MAGIC;
+ RTMemFree(pThis);
+}
+
+
+RTR0DECL(uint32_t) RTR0DbgKrnlInfoRelease(RTDBGKRNLINFO hKrnlInfo)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ if (pThis == NIL_RTDBGKRNLINFO)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (cRefs == 0)
+ rtR0DbgKrnlNtDtor(pThis);
+ return cRefs;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQueryMember(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszStructure,
+ const char *pszMember, size_t *poffMember)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszMember, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pszModule, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszStructure, VERR_INVALID_POINTER);
+ AssertPtrReturn(poffMember, VERR_INVALID_POINTER);
+ return VERR_NOT_FOUND;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQuerySymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszSymbol, void **ppvSymbol)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszSymbol, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(ppvSymbol, VERR_INVALID_PARAMETER);
+
+ RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: pszModule=%s pszSymbol=%s\n", pszModule ? pszModule : "<null>", pszSymbol));
+
+ void *pvTmpSymbol = NULL;
+ if (!ppvSymbol)
+ ppvSymbol = &pvTmpSymbol;
+
+ int rc;
+ if (!pszModule)
+ {
+ /*
+ * Search both ntoskrnl and hal, may use MmGetSystemRoutineAddress as fallback.
+ * Note! MmGetSystemRoutineAddress was buggy before XP SP2 according to Geoff Chappell.
+ */
+ if (g_NtOsKrnlInfo.pbImageBase)
+ rc = VINF_SUCCESS;
+ else
+ rc = rtR0DbgKrnlNtInit(NULL);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(g_NtOsKrnlInfo.fOkay);
+ Assert(g_HalInfo.fOkay);
+ //RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: Calling RTR0DbgKrnlInfoQuerySymbol on NT kernel...\n"));
+ rc = rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, pszSymbol, 0, ppvSymbol);
+ if (RT_FAILURE(rc))
+ {
+ //RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: Calling RTR0DbgKrnlInfoQuerySymbol on HAL kernel...\n"));
+ rc = rtR0DbgKrnlInfoLookupSymbol(&g_HalInfo, pszSymbol, 0, ppvSymbol);
+ }
+ RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: #1 returns %d *ppvSymbol=%p\n", rc, *ppvSymbol));
+ }
+ else
+ {
+ /* Init failed. Try resolve symbol, but preserve the status code up to a point. */
+ int rc2 = VERR_SYMBOL_NOT_FOUND;
+ if (g_NtOsKrnlInfo.fOkay)
+ rc2 = rtR0DbgKrnlInfoLookupSymbol(&g_NtOsKrnlInfo, pszSymbol, 0, ppvSymbol);
+ if (g_HalInfo.fOkay && rc2 == VERR_SYMBOL_NOT_FOUND)
+ rc2 = rtR0DbgKrnlInfoLookupSymbol(&g_HalInfo, pszSymbol, 0, ppvSymbol);
+ if ( rc2 == VERR_SYMBOL_NOT_FOUND
+ && g_pfnMmGetSystemRoutineAddress)
+ {
+ /* We'll overwrite init failure status code here since
+ MmGetSystemRoutineAddress will do the job for us. */
+ size_t cwcSymbol;
+ PRTUTF16 pwszSymbol = NULL;
+ rc = RTStrToUtf16Ex(pszSymbol, RTSTR_MAX, &pwszSymbol, 0, &cwcSymbol);
+ if (RT_SUCCESS(rc))
+ {
+ UNICODE_STRING UniStr;
+ UniStr.Buffer = pwszSymbol;
+ UniStr.Length = (uint16_t)(cwcSymbol * sizeof(RTUTF16));
+ UniStr.MaximumLength = UniStr.Length + sizeof(RTUTF16);
+ *ppvSymbol = g_pfnMmGetSystemRoutineAddress(&UniStr);
+ if (*ppvSymbol)
+ rc = VINF_SUCCESS;
+ else
+ rc = VERR_SYMBOL_NOT_FOUND;
+ RTUtf16Free(pwszSymbol);
+ RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: #2 returns %d *ppvSymbol=%p\n", rc, *ppvSymbol));
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Search specified module.
+ */
+ rc = VERR_MODULE_NOT_FOUND;
+ PRTDBGNTKRNLMODINFO pModInfo;
+ if (RTStrICmpAscii(pszModule, g_NtOsKrnlInfo.szName) == 0)
+ pModInfo = &g_NtOsKrnlInfo;
+ else if (RTStrICmpAscii(pszModule, g_HalInfo.szName) == 0)
+ pModInfo = &g_NtOsKrnlInfo;
+ else
+ {
+ pModInfo = NULL;
+ for (unsigned i = 0; i < pThis->cModules; i++)
+ if (RTStrICmpAscii(pszModule, pThis->aModules[i].szName) == 0)
+ {
+ pModInfo = &pThis->aModules[i];
+ break;
+ }
+ if (!pModInfo)
+ {
+ /*
+ * Not found, try load it. If module table is full, drop the first
+ * entry and shuffle the other up to make space.
+ */
+ size_t const cchModule = strlen(pszModule);
+ RTDBGNTKRNLMODINFO NewModInfo;
+ if (cchModule < sizeof(NewModInfo.szName))
+ {
+ RT_ZERO(NewModInfo);
+ memcpy(NewModInfo.szName, pszModule, cchModule);
+ NewModInfo.szName[cchModule] = '\0';
+
+ rc = rtR0DbgKrnlNtInit(&NewModInfo);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(NewModInfo.fOkay);
+ uint32_t iModule = pThis->cModules;
+ if (iModule >= RT_ELEMENTS(pThis->aModules))
+ {
+ iModule = RT_ELEMENTS(pThis->aModules) - 1;
+ memmove(&pThis->aModules[0], &pThis->aModules[1], iModule * sizeof(pThis->aModules[0]));
+ }
+ pThis->aModules[iModule] = NewModInfo;
+ pThis->cModules = iModule + 1;
+ pModInfo = &pThis->aModules[iModule];
+ rc = VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("cchModule=%zu pszModule=%s\n", cchModule, pszModule));
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ }
+ }
+ if (pModInfo)
+ {
+ rc = rtR0DbgKrnlInfoLookupSymbol(pModInfo, pszSymbol, 0, ppvSymbol);
+ RTR0DBG_NT_DEBUG_LOG(("RTR0DbgKrnlInfoQuerySymbol: #3 returns %d *ppvSymbol=%p\n", rc, *ppvSymbol));
+ }
+ }
+ return rc;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQuerySize(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszType, size_t *pcbType)
+{
+ RTDBGKRNLINFOINT *pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrNullReturn(pszModule, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbType, VERR_INVALID_POINTER);
+ return VERR_NOT_FOUND;
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp
new file mode 100644
index 00000000..2d9e9a52
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/initterm-r0drv-nt.cpp
@@ -0,0 +1,534 @@
+/* $Id: initterm-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Initialization & Termination, R0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/dbg.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include "internal/initterm.h"
+#include "internal-r0drv-nt.h"
+#include "symdb.h"
+#include "symdbdata.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** ExAllocatePoolWithTag, introduced in W2K. */
+decltype(ExAllocatePoolWithTag) *g_pfnrtExAllocatePoolWithTag;
+/** ExFreePoolWithTag, introduced in W2K. */
+decltype(ExFreePoolWithTag) *g_pfnrtExFreePoolWithTag;
+/** ExSetTimerResolution, introduced in W2K. */
+PFNMYEXSETTIMERRESOLUTION g_pfnrtNtExSetTimerResolution;
+/** ExAllocateTimer, introduced in Windows 8.1 */
+PFNEXALLOCATETIMER g_pfnrtExAllocateTimer;
+/** ExDeleteTimer, introduced in Windows 8.1 */
+PFNEXDELETETIMER g_pfnrtExDeleteTimer;
+/** ExSetTimer, introduced in Windows 8.1 */
+PFNEXSETTIMER g_pfnrtExSetTimer;
+/** ExCancelTimer, introduced in Windows 8.1 */
+PFNEXCANCELTIMER g_pfnrtExCancelTimer;
+/** KeFlushQueuedDpcs, introduced in XP. */
+PFNMYKEFLUSHQUEUEDDPCS g_pfnrtNtKeFlushQueuedDpcs;
+/** HalRequestIpi, version introduced with windows 7. */
+PFNHALREQUESTIPI_W7PLUS g_pfnrtHalRequestIpiW7Plus;
+/** HalRequestIpi, version valid up to windows vista?? */
+PFNHALREQUESTIPI_PRE_W7 g_pfnrtHalRequestIpiPreW7;
+/** Worker for RTMpPokeCpu. */
+PFNRTSENDIPI g_pfnrtMpPokeCpuWorker;
+/** KeIpiGenericCall - Introduced in Windows Server 2003. */
+PFNRTKEIPIGENERICCALL g_pfnrtKeIpiGenericCall;
+/** KeSetTargetProcessorDpcEx - Introduced in Windows 7. */
+PFNKESETTARGETPROCESSORDPCEX g_pfnrtKeSetTargetProcessorDpcEx;
+/** KeInitializeAffinityEx - Introducted in Windows 7. */
+PFNKEINITIALIZEAFFINITYEX g_pfnrtKeInitializeAffinityEx;
+/** KeAddProcessorAffinityEx - Introducted in Windows 7. */
+PFNKEADDPROCESSORAFFINITYEX g_pfnrtKeAddProcessorAffinityEx;
+/** KeGetProcessorIndexFromNumber - Introducted in Windows 7. */
+PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnrtKeGetProcessorIndexFromNumber;
+/** KeGetProcessorNumberFromIndex - Introducted in Windows 7. */
+PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnrtKeGetProcessorNumberFromIndex;
+/** KeGetCurrentProcessorNumberEx - Introducted in Windows 7. */
+PFNKEGETCURRENTPROCESSORNUMBEREX g_pfnrtKeGetCurrentProcessorNumberEx;
+/** KeQueryActiveProcessors - Introducted in Windows 2000. */
+PFNKEQUERYACTIVEPROCESSORS g_pfnrtKeQueryActiveProcessors;
+/** KeQueryMaximumProcessorCount - Introducted in Vista and obsoleted W7. */
+PFNKEQUERYMAXIMUMPROCESSORCOUNT g_pfnrtKeQueryMaximumProcessorCount;
+/** KeQueryMaximumProcessorCountEx - Introducted in Windows 7. */
+PFNKEQUERYMAXIMUMPROCESSORCOUNTEX g_pfnrtKeQueryMaximumProcessorCountEx;
+/** KeQueryMaximumGroupCount - Introducted in Windows 7. */
+PFNKEQUERYMAXIMUMGROUPCOUNT g_pfnrtKeQueryMaximumGroupCount;
+/** KeQueryActiveProcessorCount - Introducted in Vista and obsoleted W7. */
+PFNKEQUERYACTIVEPROCESSORCOUNT g_pfnrtKeQueryActiveProcessorCount;
+/** KeQueryActiveProcessorCountEx - Introducted in Windows 7. */
+PFNKEQUERYACTIVEPROCESSORCOUNTEX g_pfnrtKeQueryActiveProcessorCountEx;
+/** KeQueryLogicalProcessorRelationship - Introducted in Windows 7. */
+PFNKEQUERYLOGICALPROCESSORRELATIONSHIP g_pfnrtKeQueryLogicalProcessorRelationship;
+/** KeRegisterProcessorChangeCallback - Introducted in Windows 7. */
+PFNKEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeRegisterProcessorChangeCallback;
+/** KeDeregisterProcessorChangeCallback - Introducted in Windows 7. */
+PFNKEDEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeDeregisterProcessorChangeCallback;
+/** KeSetImportanceDpc - Introducted in NT 3.51. */
+decltype(KeSetImportanceDpc) *g_pfnrtKeSetImportanceDpc;
+/** KeSetTargetProcessorDpc - Introducted in NT 3.51. */
+decltype(KeSetTargetProcessorDpc) *g_pfnrtKeSetTargetProcessorDpc;
+/** KeInitializeTimerEx - Introduced in NT 4. */
+decltype(KeInitializeTimerEx) *g_pfnrtKeInitializeTimerEx;
+/** KeShouldYieldProcessor - Introduced in Windows 10. */
+PFNKESHOULDYIELDPROCESSOR g_pfnrtKeShouldYieldProcessor;
+/** Pointer to the MmProtectMdlSystemAddress kernel function if it's available.
+ * This API was introduced in XP. */
+decltype(MmProtectMdlSystemAddress) *g_pfnrtMmProtectMdlSystemAddress;
+/** MmAllocatePagesForMdl - Introduced in Windows 2000. */
+decltype(MmAllocatePagesForMdl) *g_pfnrtMmAllocatePagesForMdl;
+/** MmAllocatePagesForMdlEx - Introduced in Windows Server 2003 SP1. */
+PFNMMALLOCATEPAGESFORMDLEX g_pfnrtMmAllocatePagesForMdlEx;
+/** MmFreePagesFromMdl - Introduced in Windows 2000. */
+decltype(MmFreePagesFromMdl) *g_pfnrtMmFreePagesFromMdl;
+/** MmMapLockedPagesSpecifyCache - Introduced in Windows NT4 SP4. */
+decltype(MmMapLockedPagesSpecifyCache) *g_pfnrtMmMapLockedPagesSpecifyCache;
+/** MmAllocateContiguousMemorySpecifyCache - Introduced in Windows 2000. */
+decltype(MmAllocateContiguousMemorySpecifyCache) *g_pfnrtMmAllocateContiguousMemorySpecifyCache;
+/** MmSecureVirtualMemory - Introduced in NT 3.51. */
+decltype(MmSecureVirtualMemory) *g_pfnrtMmSecureVirtualMemory;
+/** MmUnsecureVirtualMemory - Introduced in NT 3.51. */
+decltype(MmUnsecureVirtualMemory) *g_pfnrtMmUnsecureVirtualMemory;
+/** PsIsThreadTerminating - Introduced in NT 3.50. */
+decltype(PsIsThreadTerminating) *g_pfnrtPsIsThreadTerminating;
+/** RtlGetVersion, introduced in ??. */
+PFNRTRTLGETVERSION g_pfnrtRtlGetVersion;
+#ifdef RT_ARCH_X86
+/** KeQueryInterruptTime - exported/new in Windows 2000. */
+PFNRTKEQUERYINTERRUPTTIME g_pfnrtKeQueryInterruptTime;
+#endif
+/** KeQueryInterruptTimePrecise - new in Windows 8. */
+PFNRTKEQUERYINTERRUPTTIMEPRECISE g_pfnrtKeQueryInterruptTimePrecise;
+/** KeQuerySystemTimePrecise - new in Windows 8. */
+PFNRTKEQUERYSYSTEMTIMEPRECISE g_pfnrtKeQuerySystemTimePrecise;
+
+/** Offset of the _KPRCB::QuantumEnd field. 0 if not found. */
+uint32_t g_offrtNtPbQuantumEnd;
+/** Size of the _KPRCB::QuantumEnd field. 0 if not found. */
+uint32_t g_cbrtNtPbQuantumEnd;
+/** Offset of the _KPRCB::DpcQueueDepth field. 0 if not found. */
+uint32_t g_offrtNtPbDpcQueueDepth;
+
+/** The combined NT version, see RTNT_MAKE_VERSION. */
+uint32_t g_uRtNtVersion = RTNT_MAKE_VERSION(4, 0);
+/** The major version number. */
+uint8_t g_uRtNtMajorVer;
+/** The minor version number. */
+uint8_t g_uRtNtMinorVer;
+/** The build number. */
+uint32_t g_uRtNtBuildNo;
+
+/** Pointer to the MmHighestUserAddress kernel variable - can be NULL. */
+uintptr_t const *g_puRtMmHighestUserAddress;
+/** Pointer to the MmSystemRangeStart kernel variable - can be NULL. */
+uintptr_t const *g_puRtMmSystemRangeStart;
+
+
+/**
+ * Determines the NT kernel verison information.
+ *
+ * @param pOsVerInfo Where to return the version information.
+ *
+ * @remarks pOsVerInfo->fSmp is only definitive if @c true.
+ * @remarks pOsVerInfo->uCsdNo is set to MY_NIL_CSD if it cannot be determined.
+ */
+static void rtR0NtGetOsVersionInfo(PRTNTSDBOSVER pOsVerInfo)
+{
+ ULONG ulMajorVersion = 0;
+ ULONG ulMinorVersion = 0;
+ ULONG ulBuildNumber = 0;
+
+ pOsVerInfo->fChecked = PsGetVersion(&ulMajorVersion, &ulMinorVersion, &ulBuildNumber, NULL) == TRUE;
+ pOsVerInfo->uMajorVer = (uint8_t)ulMajorVersion;
+ pOsVerInfo->uMinorVer = (uint8_t)ulMinorVersion;
+ pOsVerInfo->uBuildNo = ulBuildNumber;
+#define MY_NIL_CSD 0x3f
+ pOsVerInfo->uCsdNo = MY_NIL_CSD;
+
+ if (g_pfnrtRtlGetVersion)
+ {
+ RTL_OSVERSIONINFOEXW VerInfo;
+ RT_ZERO(VerInfo);
+ VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
+
+ NTSTATUS rcNt = g_pfnrtRtlGetVersion(&VerInfo);
+ if (NT_SUCCESS(rcNt))
+ pOsVerInfo->uCsdNo = VerInfo.wServicePackMajor;
+ }
+
+ /* Note! We cannot quite say if something is MP or UNI. So, fSmp is
+ redefined to indicate that it must be MP.
+ Note! RTMpGetCount is not available here. */
+ pOsVerInfo->fSmp = ulMajorVersion >= 6; /* Vista and later has no UNI kernel AFAIK. */
+ if (!pOsVerInfo->fSmp)
+ {
+ if ( g_pfnrtKeQueryMaximumProcessorCountEx
+ && g_pfnrtKeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS) > 1)
+ pOsVerInfo->fSmp = true;
+ else if ( g_pfnrtKeQueryMaximumProcessorCount
+ && g_pfnrtKeQueryMaximumProcessorCount() > 1)
+ pOsVerInfo->fSmp = true;
+ else if ( g_pfnrtKeQueryActiveProcessors
+ && g_pfnrtKeQueryActiveProcessors() > 1)
+ pOsVerInfo->fSmp = true;
+ else if (KeNumberProcessors > 1)
+ pOsVerInfo->fSmp = true;
+ }
+}
+
+
+/**
+ * Tries a set against the current kernel.
+ *
+ * @retval true if it matched up, global variables are updated.
+ * @retval false otherwise (no globals updated).
+ * @param pSet The data set.
+ * @param pbPrcb Pointer to the processor control block.
+ * @param pszVendor Pointer to the processor vendor string.
+ * @param pOsVerInfo The OS version info.
+ */
+static bool rtR0NtTryMatchSymSet(PCRTNTSDBSET pSet, uint8_t *pbPrcb, const char *pszVendor, PCRTNTSDBOSVER pOsVerInfo)
+{
+ /*
+ * Don't bother trying stuff where the NT kernel version number differs, or
+ * if the build type or SMPness doesn't match up.
+ */
+ if ( pSet->OsVerInfo.uMajorVer != pOsVerInfo->uMajorVer
+ || pSet->OsVerInfo.uMinorVer != pOsVerInfo->uMinorVer
+ || pSet->OsVerInfo.fChecked != pOsVerInfo->fChecked
+ || (!pSet->OsVerInfo.fSmp && pOsVerInfo->fSmp /*must-be-smp*/) )
+ {
+ //DbgPrint("IPRT: #%d Version/type mismatch.\n", pSet - &g_artNtSdbSets[0]);
+ return false;
+ }
+
+ /*
+ * Do the CPU vendor test.
+ *
+ * Note! The MmIsAddressValid call is the real #PF security here as the
+ * __try/__except has limited/no ability to catch everything we need.
+ */
+ char *pszPrcbVendorString = (char *)&pbPrcb[pSet->KPRCB.offVendorString];
+ if (!MmIsAddressValid(&pszPrcbVendorString[4 * 3 - 1]))
+ {
+ //DbgPrint("IPRT: #%d invalid vendor string address.\n", pSet - &g_artNtSdbSets[0]);
+ return false;
+ }
+ __try
+ {
+ if (memcmp(pszPrcbVendorString, pszVendor, RT_MIN(4 * 3, pSet->KPRCB.cbVendorString)) != 0)
+ {
+ //DbgPrint("IPRT: #%d Vendor string mismatch.\n", pSet - &g_artNtSdbSets[0]);
+ return false;
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ DbgPrint("IPRT: %#d Exception\n", pSet - &g_artNtSdbSets[0]);
+ return false;
+ }
+
+ /*
+ * Got a match, update the global variables and report success.
+ */
+ g_offrtNtPbQuantumEnd = pSet->KPRCB.offQuantumEnd;
+ g_cbrtNtPbQuantumEnd = pSet->KPRCB.cbQuantumEnd;
+ g_offrtNtPbDpcQueueDepth = pSet->KPRCB.offDpcQueueDepth;
+
+#if 0
+ DbgPrint("IPRT: Using data set #%u for %u.%usp%u build %u %s %s.\n",
+ pSet - &g_artNtSdbSets[0],
+ pSet->OsVerInfo.uMajorVer,
+ pSet->OsVerInfo.uMinorVer,
+ pSet->OsVerInfo.uCsdNo,
+ pSet->OsVerInfo.uBuildNo,
+ pSet->OsVerInfo.fSmp ? "smp" : "uni",
+ pSet->OsVerInfo.fChecked ? "checked" : "free");
+#endif
+ return true;
+}
+
+
+DECLHIDDEN(int) rtR0InitNative(void)
+{
+ /*
+ * Preinitialize g_uRtNtVersion so RTMemAlloc uses the right kind of pool
+ * when RTR0DbgKrnlInfoOpen calls it.
+ */
+ RTNTSDBOSVER OsVerInfo;
+ rtR0NtGetOsVersionInfo(&OsVerInfo);
+ g_uRtNtVersion = RTNT_MAKE_VERSION(OsVerInfo.uMajorVer, OsVerInfo.uMinorVer);
+ g_uRtNtMinorVer = OsVerInfo.uMinorVer;
+ g_uRtNtMajorVer = OsVerInfo.uMajorVer;
+ g_uRtNtBuildNo = OsVerInfo.uBuildNo;
+
+ /*
+ * Initialize the function pointers.
+ */
+ RTDBGKRNLINFO hKrnlInfo;
+ int rc = RTR0DbgKrnlInfoOpen(&hKrnlInfo, 0/*fFlags*/);
+ AssertRCReturn(rc, rc);
+
+#define GET_SYSTEM_ROUTINE_EX(a_Prf, a_Name, a_pfnType) \
+ do { RT_CONCAT3(g_pfnrt, a_Prf, a_Name) = (a_pfnType)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, #a_Name); } while (0)
+#define GET_SYSTEM_ROUTINE(a_Name) GET_SYSTEM_ROUTINE_EX(RT_NOTHING, a_Name, decltype(a_Name) *)
+#define GET_SYSTEM_ROUTINE_PRF(a_Prf,a_Name) GET_SYSTEM_ROUTINE_EX(a_Prf, a_Name, decltype(a_Name) *)
+#define GET_SYSTEM_ROUTINE_TYPE(a_Name, a_pfnType) GET_SYSTEM_ROUTINE_EX(RT_NOTHING, a_Name, a_pfnType)
+
+ GET_SYSTEM_ROUTINE(ExAllocatePoolWithTag);
+ GET_SYSTEM_ROUTINE(ExFreePoolWithTag);
+ GET_SYSTEM_ROUTINE_PRF(Nt,ExSetTimerResolution);
+ GET_SYSTEM_ROUTINE_TYPE(ExAllocateTimer, PFNEXALLOCATETIMER);
+ GET_SYSTEM_ROUTINE_TYPE(ExDeleteTimer, PFNEXDELETETIMER);
+ GET_SYSTEM_ROUTINE_TYPE(ExSetTimer, PFNEXSETTIMER);
+ GET_SYSTEM_ROUTINE_TYPE(ExCancelTimer, PFNEXCANCELTIMER);
+ GET_SYSTEM_ROUTINE_PRF(Nt,KeFlushQueuedDpcs);
+ GET_SYSTEM_ROUTINE(KeIpiGenericCall);
+ GET_SYSTEM_ROUTINE(KeSetTargetProcessorDpcEx);
+ GET_SYSTEM_ROUTINE(KeInitializeAffinityEx);
+ GET_SYSTEM_ROUTINE(KeAddProcessorAffinityEx);
+ GET_SYSTEM_ROUTINE_TYPE(KeGetProcessorIndexFromNumber, PFNKEGETPROCESSORINDEXFROMNUMBER);
+ GET_SYSTEM_ROUTINE(KeGetProcessorNumberFromIndex);
+ GET_SYSTEM_ROUTINE_TYPE(KeGetCurrentProcessorNumberEx, PFNKEGETCURRENTPROCESSORNUMBEREX);
+ GET_SYSTEM_ROUTINE(KeQueryActiveProcessors);
+ GET_SYSTEM_ROUTINE(KeQueryMaximumProcessorCount);
+ GET_SYSTEM_ROUTINE(KeQueryMaximumProcessorCountEx);
+ GET_SYSTEM_ROUTINE(KeQueryMaximumGroupCount);
+ GET_SYSTEM_ROUTINE(KeQueryActiveProcessorCount);
+ GET_SYSTEM_ROUTINE(KeQueryActiveProcessorCountEx);
+ GET_SYSTEM_ROUTINE(KeQueryLogicalProcessorRelationship);
+ GET_SYSTEM_ROUTINE(KeRegisterProcessorChangeCallback);
+ GET_SYSTEM_ROUTINE(KeDeregisterProcessorChangeCallback);
+ GET_SYSTEM_ROUTINE(KeSetImportanceDpc);
+ GET_SYSTEM_ROUTINE(KeSetTargetProcessorDpc);
+ GET_SYSTEM_ROUTINE(KeInitializeTimerEx);
+ GET_SYSTEM_ROUTINE_TYPE(KeShouldYieldProcessor, PFNKESHOULDYIELDPROCESSOR);
+ GET_SYSTEM_ROUTINE(MmProtectMdlSystemAddress);
+ GET_SYSTEM_ROUTINE(MmAllocatePagesForMdl);
+ GET_SYSTEM_ROUTINE(MmAllocatePagesForMdlEx);
+ GET_SYSTEM_ROUTINE(MmFreePagesFromMdl);
+ GET_SYSTEM_ROUTINE(MmMapLockedPagesSpecifyCache);
+ GET_SYSTEM_ROUTINE(MmAllocateContiguousMemorySpecifyCache);
+ GET_SYSTEM_ROUTINE(MmSecureVirtualMemory);
+ GET_SYSTEM_ROUTINE(MmUnsecureVirtualMemory);
+
+ GET_SYSTEM_ROUTINE_TYPE(RtlGetVersion, PFNRTRTLGETVERSION);
+#ifdef RT_ARCH_X86
+ GET_SYSTEM_ROUTINE(KeQueryInterruptTime);
+#endif
+ GET_SYSTEM_ROUTINE_TYPE(KeQueryInterruptTimePrecise, PFNRTKEQUERYINTERRUPTTIMEPRECISE);
+ GET_SYSTEM_ROUTINE_TYPE(KeQuerySystemTimePrecise, PFNRTKEQUERYSYSTEMTIMEPRECISE);
+
+ g_pfnrtHalRequestIpiW7Plus = (PFNHALREQUESTIPI_W7PLUS)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "HalRequestIpi");
+ g_pfnrtHalRequestIpiPreW7 = (PFNHALREQUESTIPI_PRE_W7)g_pfnrtHalRequestIpiW7Plus;
+
+ g_puRtMmHighestUserAddress = (uintptr_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "MmHighestUserAddress");
+ g_puRtMmSystemRangeStart = (uintptr_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "MmSystemRangeStart");
+
+#ifdef RT_ARCH_X86
+ rc = rtR0Nt3InitSymbols(hKrnlInfo);
+ RTR0DbgKrnlInfoRelease(hKrnlInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+#else
+ RTR0DbgKrnlInfoRelease(hKrnlInfo);
+#endif
+
+ /*
+ * Get and publish the definitive NT version.
+ */
+ rtR0NtGetOsVersionInfo(&OsVerInfo);
+ g_uRtNtVersion = RTNT_MAKE_VERSION(OsVerInfo.uMajorVer, OsVerInfo.uMinorVer);
+ g_uRtNtMinorVer = OsVerInfo.uMinorVer;
+ g_uRtNtMajorVer = OsVerInfo.uMajorVer;
+ g_uRtNtBuildNo = OsVerInfo.uBuildNo;
+
+
+ /*
+ * HACK ALERT! (and déjà vu warning - remember win32k.sys on OS/2?)
+ *
+ * Try find _KPRCB::QuantumEnd and _KPRCB::[DpcData.]DpcQueueDepth.
+ * For purpose of verification we use the VendorString member (12+1 chars).
+ *
+ * The offsets was initially derived by poking around with windbg
+ * (dt _KPRCB, !prcb ++, and such like). Systematic harvesting was then
+ * planned using dia2dump, grep and the symbol pack in a manner like this:
+ * dia2dump -type _KDPC_DATA -type _KPRCB EXE\ntkrnlmp.pdb | grep -wE "QuantumEnd|DpcData|DpcQueueDepth|VendorString"
+ *
+ * The final solution ended up using a custom harvester program called
+ * ntBldSymDb that recursively searches thru unpacked symbol packages for
+ * the desired structure offsets. The program assumes that the packages
+ * are unpacked into directories with the same name as the package, with
+ * exception of some of the w2k packages which requires a 'w2k' prefix to
+ * be distinguishable from another.
+ */
+
+ /*
+ * Gather consistent CPU vendor string and PRCB pointers.
+ */
+ KIRQL OldIrql;
+ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); /* make sure we stay on the same cpu */
+
+ union
+ {
+ uint32_t auRegs[4];
+ char szVendor[4*3+1];
+ } u;
+ ASMCpuId(0, &u.auRegs[3], &u.auRegs[0], &u.auRegs[2], &u.auRegs[1]);
+ u.szVendor[4*3] = '\0';
+
+ uint8_t *pbPrcb;
+ __try /* Warning. This try/except statement may provide some false safety. */
+ {
+#if defined(RT_ARCH_X86)
+ PKPCR pPcr = (PKPCR)__readfsdword(RT_UOFFSETOF(KPCR,SelfPcr));
+ pbPrcb = (uint8_t *)pPcr->Prcb;
+#elif defined(RT_ARCH_AMD64)
+ PKPCR pPcr = (PKPCR)__readgsqword(RT_UOFFSETOF(KPCR,Self));
+ pbPrcb = (uint8_t *)pPcr->CurrentPrcb;
+#else
+# error "port me"
+ pbPrcb = NULL;
+#endif
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ pbPrcb = NULL;
+ }
+
+ /*
+ * Search the database
+ */
+ if (pbPrcb)
+ {
+ /* Find the best matching kernel version based on build number. */
+ uint32_t iBest = UINT32_MAX;
+ int32_t iBestDelta = INT32_MAX;
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_artNtSdbSets); i++)
+ {
+ if (g_artNtSdbSets[i].OsVerInfo.fChecked != OsVerInfo.fChecked)
+ continue;
+ if (OsVerInfo.fSmp /*must-be-smp*/ && !g_artNtSdbSets[i].OsVerInfo.fSmp)
+ continue;
+
+ int32_t iDelta = RT_ABS((int32_t)OsVerInfo.uBuildNo - (int32_t)g_artNtSdbSets[i].OsVerInfo.uBuildNo);
+ if ( iDelta == 0
+ && (g_artNtSdbSets[i].OsVerInfo.uCsdNo == OsVerInfo.uCsdNo || OsVerInfo.uCsdNo == MY_NIL_CSD))
+ {
+ /* prefect */
+ iBestDelta = iDelta;
+ iBest = i;
+ break;
+ }
+ if ( iDelta < iBestDelta
+ || iBest == UINT32_MAX
+ || ( iDelta == iBestDelta
+ && OsVerInfo.uCsdNo != MY_NIL_CSD
+ && RT_ABS(g_artNtSdbSets[i ].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo)
+ < RT_ABS(g_artNtSdbSets[iBest].OsVerInfo.uCsdNo - (int32_t)OsVerInfo.uCsdNo)
+ )
+ )
+ {
+ iBestDelta = iDelta;
+ iBest = i;
+ }
+ }
+ if (iBest < RT_ELEMENTS(g_artNtSdbSets))
+ {
+ /* Try all sets: iBest -> End; iBest -> Start. */
+ bool fDone = false;
+ int32_t i = iBest;
+ while ( i < RT_ELEMENTS(g_artNtSdbSets)
+ && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo)))
+ i++;
+ if (!fDone)
+ {
+ i = (int32_t)iBest - 1;
+ while ( i >= 0
+ && !(fDone = rtR0NtTryMatchSymSet(&g_artNtSdbSets[i], pbPrcb, u.szVendor, &OsVerInfo)))
+ i--;
+ }
+ }
+ else
+ DbgPrint("IPRT: Failed to locate data set.\n");
+ }
+ else
+ DbgPrint("IPRT: Failed to get PCBR pointer.\n");
+
+ KeLowerIrql(OldIrql); /* Lowering the IRQL early in the hope that we may catch exceptions below. */
+
+#ifndef IN_GUEST
+ if (!g_offrtNtPbQuantumEnd && !g_offrtNtPbDpcQueueDepth)
+ DbgPrint("IPRT: Neither _KPRCB::QuantumEnd nor _KPRCB::DpcQueueDepth was not found! Kernel %u.%u %u %s\n",
+ OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free");
+# ifdef DEBUG
+ else
+ DbgPrint("IPRT: _KPRCB:{.QuantumEnd=%x/%d, .DpcQueueDepth=%x/%d} Kernel %u.%u %u %s\n",
+ g_offrtNtPbQuantumEnd, g_cbrtNtPbQuantumEnd, g_offrtNtPbDpcQueueDepth, g_offrtNtPbDpcQueueDepth,
+ OsVerInfo.uMajorVer, OsVerInfo.uMinorVer, OsVerInfo.uBuildNo, OsVerInfo.fChecked ? "checked" : "free");
+# endif
+#endif
+
+ /*
+ * Initialize multi processor stuff. This registers a callback, so
+ * we call rtR0TermNative to do the deregistration on failure.
+ */
+ rc = rtR0MpNtInit(&OsVerInfo);
+ if (RT_FAILURE(rc))
+ {
+ rtR0TermNative();
+ DbgPrint("IPRT: Fatal: rtR0MpNtInit failed: %d\n", rc);
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0TermNative(void)
+{
+ rtR0MpNtTerm();
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h b/src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h
new file mode 100644
index 00000000..f2a63e41
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/internal-r0drv-nt.h
@@ -0,0 +1,207 @@
+/* $Id: internal-r0drv-nt.h $ */
+/** @file
+ * IPRT - Internal Header for the NT Ring-0 Driver 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
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r0drv_nt_internal_r0drv_nt_h
+#define IPRT_INCLUDED_SRC_r0drv_nt_internal_r0drv_nt_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cpuset.h>
+#include <iprt/nt/nt.h>
+
+RT_C_DECLS_BEGIN
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef ULONG (__stdcall *PFNMYEXSETTIMERRESOLUTION)(ULONG, BOOLEAN);
+typedef VOID (__stdcall *PFNMYKEFLUSHQUEUEDDPCS)(VOID);
+typedef VOID (__stdcall *PFNHALSENDSOFTWAREINTERRUPT)(ULONG ProcessorNumber, KIRQL Irql);
+typedef int (__stdcall *PFNRTSENDIPI)(RTCPUID idCpu);
+typedef ULONG_PTR (__stdcall *PFNRTKEIPIGENERICCALL)(PKIPI_BROADCAST_WORKER BroadcastFunction, ULONG_PTR Context);
+typedef ULONG (__stdcall *PFNRTRTLGETVERSION)(PRTL_OSVERSIONINFOEXW pVerInfo);
+#ifndef RT_ARCH_AMD64
+typedef ULONGLONG (__stdcall *PFNRTKEQUERYINTERRUPTTIME)(VOID);
+typedef VOID (__stdcall *PFNRTKEQUERYSYSTEMTIME)(PLARGE_INTEGER pTime);
+#endif
+typedef ULONG64 (__stdcall *PFNRTKEQUERYINTERRUPTTIMEPRECISE)(PULONG64 pQpcTS);
+typedef VOID (__stdcall *PFNRTKEQUERYSYSTEMTIMEPRECISE)(PLARGE_INTEGER pTime);
+typedef PMDL (__stdcall *PFNMMALLOCATEPAGESFORMDLEX)(PHYSICAL_ADDRESS, PHYSICAL_ADDRESS, PHYSICAL_ADDRESS,
+ SIZE_T, MEMORY_CACHING_TYPE, ULONG);
+
+#ifndef EX_TIMER_HIGH_RESOLUTION /* Too old DDK, so add missing bits. */
+# define EX_TIMER_NO_WAKE RT_BIT_32(3)
+# define EX_TIMER_HIGH_RESOLUTION RT_BIT_32(2)
+# define EX_TIMER_NOTIFICATION RT_BIT_32(31)
+typedef struct _EX_TIMER *PEX_TIMER;
+typedef VOID (__stdcall *PEXT_CALLBACK)(PEX_TIMER, void *);
+typedef PEX_TIMER (__stdcall *PFNEXALLOCATETIMER)(PEXT_CALLBACK pfnCallback, void *pvUser, ULONG fFlags);
+
+typedef VOID (__stdcall *PEXT_DELETE_CALLBACK)(void *);
+typedef struct _EXT_DELETE_PARAMETERS
+{
+ ULONG Version;
+ ULONG Reserved;
+ PEXT_DELETE_CALLBACK DeleteCallback;
+ void *DeleteContext;
+} EXT_DELETE_PARAMETERS;
+typedef EXT_DELETE_PARAMETERS *PEXT_DELETE_PARAMETERS;
+DECLINLINE(void) ExInitializeDeleteTimerParameters(PEXT_DELETE_PARAMETERS pParams)
+{
+ pParams->Version = 0;
+ pParams->Reserved = 0;
+ pParams->DeleteCallback = NULL;
+ pParams->DeleteContext = NULL;
+}
+typedef BOOLEAN (__stdcall *PFNEXDELETETIMER)(PEX_TIMER pTimer, BOOLEAN fCancel, BOOLEAN fWait, PEXT_DELETE_PARAMETERS pParams);
+
+typedef struct _EXT_SET_PARAMETERS_V0
+{
+ ULONG Version;
+ ULONG Reserved;
+ LONGLONG NoWakeTolerance;
+} EXT_SET_PARAMETERS;
+typedef EXT_SET_PARAMETERS *PEXT_SET_PARAMETERS;
+DECLINLINE(void) ExInitializeSetTimerParameters(PEXT_SET_PARAMETERS pParams)
+{
+ pParams->Version = 0;
+ pParams->Reserved = 0;
+ pParams->NoWakeTolerance = 0;
+}
+typedef BOOLEAN (__stdcall *PFNEXSETTIMER)(PEX_TIMER pTimer, LONGLONG DueTime, LONGLONG Period, PEXT_SET_PARAMETERS pParams);
+
+typedef BOOLEAN (__stdcall *PFNEXCANCELTIMER)(PEX_TIMER pTimer, void *pvReserved);
+
+#else
+typedef decltype(ExAllocateTimer) *PFNEXALLOCATETIMER;
+typedef decltype(ExDeleteTimer) *PFNEXDELETETIMER;
+typedef decltype(ExSetTimer) *PFNEXSETTIMER;
+typedef decltype(ExCancelTimer) *PFNEXCANCELTIMER;
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern RTCPUSET g_rtMpNtCpuSet;
+extern uint32_t g_cRtMpNtMaxGroups;
+extern uint32_t g_cRtMpNtMaxCpus;
+extern RTCPUID g_aidRtMpNtByCpuSetIdx[RTCPUSET_MAX_CPUS];
+
+extern decltype(ExAllocatePoolWithTag) *g_pfnrtExAllocatePoolWithTag;
+extern decltype(ExFreePoolWithTag) *g_pfnrtExFreePoolWithTag;
+extern PFNMYEXSETTIMERRESOLUTION g_pfnrtNtExSetTimerResolution;
+extern PFNEXALLOCATETIMER g_pfnrtExAllocateTimer;
+extern PFNEXDELETETIMER g_pfnrtExDeleteTimer;
+extern PFNEXSETTIMER g_pfnrtExSetTimer;
+extern PFNEXCANCELTIMER g_pfnrtExCancelTimer;
+extern PFNMYKEFLUSHQUEUEDDPCS g_pfnrtNtKeFlushQueuedDpcs;
+extern PFNHALREQUESTIPI_W7PLUS g_pfnrtHalRequestIpiW7Plus;
+extern PFNHALREQUESTIPI_PRE_W7 g_pfnrtHalRequestIpiPreW7;
+extern PFNHALSENDSOFTWAREINTERRUPT g_pfnrtNtHalSendSoftwareInterrupt;
+extern PFNRTSENDIPI g_pfnrtMpPokeCpuWorker;
+extern PFNRTKEIPIGENERICCALL g_pfnrtKeIpiGenericCall;
+extern PFNKESETTARGETPROCESSORDPCEX g_pfnrtKeSetTargetProcessorDpcEx;
+extern PFNKEINITIALIZEAFFINITYEX g_pfnrtKeInitializeAffinityEx;
+extern PFNKEADDPROCESSORAFFINITYEX g_pfnrtKeAddProcessorAffinityEx;
+extern PFNKEGETPROCESSORINDEXFROMNUMBER g_pfnrtKeGetProcessorIndexFromNumber;
+extern PFNKEGETPROCESSORNUMBERFROMINDEX g_pfnrtKeGetProcessorNumberFromIndex;
+extern PFNKEGETCURRENTPROCESSORNUMBEREX g_pfnrtKeGetCurrentProcessorNumberEx;
+extern PFNKEQUERYACTIVEPROCESSORS g_pfnrtKeQueryActiveProcessors;
+extern PFNKEQUERYMAXIMUMPROCESSORCOUNT g_pfnrtKeQueryMaximumProcessorCount;
+extern PFNKEQUERYMAXIMUMPROCESSORCOUNTEX g_pfnrtKeQueryMaximumProcessorCountEx;
+extern PFNKEQUERYMAXIMUMGROUPCOUNT g_pfnrtKeQueryMaximumGroupCount;
+extern PFNKEQUERYACTIVEPROCESSORCOUNT g_pfnrtKeQueryActiveProcessorCount;
+extern PFNKEQUERYACTIVEPROCESSORCOUNTEX g_pfnrtKeQueryActiveProcessorCountEx;
+extern PFNKEQUERYLOGICALPROCESSORRELATIONSHIP g_pfnrtKeQueryLogicalProcessorRelationship;
+extern PFNKEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeRegisterProcessorChangeCallback;
+extern PFNKEDEREGISTERPROCESSORCHANGECALLBACK g_pfnrtKeDeregisterProcessorChangeCallback;
+extern decltype(KeSetImportanceDpc) *g_pfnrtKeSetImportanceDpc;
+extern decltype(KeSetTargetProcessorDpc) *g_pfnrtKeSetTargetProcessorDpc;
+extern decltype(KeInitializeTimerEx) *g_pfnrtKeInitializeTimerEx;
+extern PFNKESHOULDYIELDPROCESSOR g_pfnrtKeShouldYieldProcessor;
+extern decltype(MmProtectMdlSystemAddress) *g_pfnrtMmProtectMdlSystemAddress;
+extern decltype(MmAllocatePagesForMdl) *g_pfnrtMmAllocatePagesForMdl;
+extern PFNMMALLOCATEPAGESFORMDLEX g_pfnrtMmAllocatePagesForMdlEx;
+extern decltype(MmFreePagesFromMdl) *g_pfnrtMmFreePagesFromMdl;
+extern decltype(MmMapLockedPagesSpecifyCache) *g_pfnrtMmMapLockedPagesSpecifyCache;
+extern decltype(MmAllocateContiguousMemorySpecifyCache) *g_pfnrtMmAllocateContiguousMemorySpecifyCache;
+extern decltype(MmSecureVirtualMemory) *g_pfnrtMmSecureVirtualMemory;
+extern decltype(MmUnsecureVirtualMemory) *g_pfnrtMmUnsecureVirtualMemory;
+extern decltype(PsIsThreadTerminating) *g_pfnrtPsIsThreadTerminating;
+
+extern PFNRTRTLGETVERSION g_pfnrtRtlGetVersion;
+#ifdef RT_ARCH_X86
+extern PFNRTKEQUERYINTERRUPTTIME g_pfnrtKeQueryInterruptTime;
+#endif
+extern PFNRTKEQUERYINTERRUPTTIMEPRECISE g_pfnrtKeQueryInterruptTimePrecise;
+extern PFNRTKEQUERYSYSTEMTIMEPRECISE g_pfnrtKeQuerySystemTimePrecise;
+
+extern uint32_t g_offrtNtPbQuantumEnd;
+extern uint32_t g_cbrtNtPbQuantumEnd;
+extern uint32_t g_offrtNtPbDpcQueueDepth;
+
+/** Makes an NT version for checking against g_uRtNtVersion. */
+#define RTNT_MAKE_VERSION(uMajor, uMinor) RT_MAKE_U32(uMinor, uMajor)
+
+extern uint32_t g_uRtNtVersion;
+extern uint8_t g_uRtNtMajorVer;
+extern uint8_t g_uRtNtMinorVer;
+extern uint32_t g_uRtNtBuildNo;
+
+extern uintptr_t const *g_puRtMmHighestUserAddress;
+extern uintptr_t const *g_puRtMmSystemRangeStart;
+
+
+int __stdcall rtMpPokeCpuUsingFailureNotSupported(RTCPUID idCpu);
+int __stdcall rtMpPokeCpuUsingDpc(RTCPUID idCpu);
+int __stdcall rtMpPokeCpuUsingBroadcastIpi(RTCPUID idCpu);
+int __stdcall rtMpPokeCpuUsingHalRequestIpiW7Plus(RTCPUID idCpu);
+int __stdcall rtMpPokeCpuUsingHalRequestIpiPreW7(RTCPUID idCpu);
+
+struct RTNTSDBOSVER;
+DECLHIDDEN(int) rtR0MpNtInit(struct RTNTSDBOSVER const *pOsVerInfo);
+DECLHIDDEN(void) rtR0MpNtTerm(void);
+DECLHIDDEN(int) rtMpNtSetTargetProcessorDpc(KDPC *pDpc, RTCPUID idCpu);
+#if defined(RT_ARCH_X86) && defined(NIL_RTDBGKRNLINFO)
+DECLHIDDEN(int) rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo);
+#endif
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_internal_r0drv_nt_h */
+
diff --git a/src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp
new file mode 100644
index 00000000..2ec319d7
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/memobj-r0drv-nt.cpp
@@ -0,0 +1,1284 @@
+/* $Id: memobj-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+
+#include <iprt/memobj.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/process.h>
+#include "internal/memobj.h"
+#include "internal-r0drv-nt.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Maximum number of bytes we try to lock down in one go.
+ * This is supposed to have a limit right below 256MB, but this appears
+ * to actually be much lower. The values here have been determined experimentally.
+ */
+#ifdef RT_ARCH_X86
+# define MAX_LOCK_MEM_SIZE (32*1024*1024) /* 32MB */
+#endif
+#ifdef RT_ARCH_AMD64
+# define MAX_LOCK_MEM_SIZE (24*1024*1024) /* 24MB */
+#endif
+
+/* Newer WDK constants: */
+#ifndef MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS
+# define MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS 0x20
+#endif
+#ifndef MM_ALLOCATE_FAST_LARGE_PAGES
+# define MM_ALLOCATE_FAST_LARGE_PAGES 0x40
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The NT version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJNT
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ /** Used MmAllocatePagesForMdl(). */
+ bool fAllocatedPagesForMdl;
+ /** Set if this is sub-section of the parent. */
+ bool fSubMapping;
+ /** Pointer returned by MmSecureVirtualMemory */
+ PVOID pvSecureMem;
+ /** The number of PMDLs (memory descriptor lists) in the array. */
+ uint32_t cMdls;
+ /** Array of MDL pointers. (variable size) */
+ PMDL apMdls[1];
+} RTR0MEMOBJNT;
+/** Pointer to the NT version of the memory object structure. */
+typedef RTR0MEMOBJNT *PRTR0MEMOBJNT;
+
+
+
+DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)pMem;
+
+ /*
+ * Deal with it on a per type basis (just as a variation).
+ */
+ switch (pMemNt->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_LOW:
+ if (pMemNt->fAllocatedPagesForMdl)
+ {
+ Assert(pMemNt->Core.pv && pMemNt->cMdls == 1 && pMemNt->apMdls[0]);
+ MmUnmapLockedPages(pMemNt->Core.pv, pMemNt->apMdls[0]);
+ pMemNt->Core.pv = NULL;
+ if (pMemNt->pvSecureMem)
+ {
+ g_pfnrtMmUnsecureVirtualMemory(pMemNt->pvSecureMem);
+ pMemNt->pvSecureMem = NULL;
+ }
+
+ g_pfnrtMmFreePagesFromMdl(pMemNt->apMdls[0]);
+ ExFreePool(pMemNt->apMdls[0]);
+ pMemNt->apMdls[0] = NULL;
+ pMemNt->cMdls = 0;
+ break;
+ }
+ AssertFailed();
+ break;
+
+ case RTR0MEMOBJTYPE_PAGE:
+ Assert(pMemNt->Core.pv);
+ if (pMemNt->fAllocatedPagesForMdl)
+ {
+ Assert(pMemNt->Core.pv && pMemNt->cMdls == 1 && pMemNt->apMdls[0]);
+ Assert(pMemNt->pvSecureMem == NULL);
+ MmUnmapLockedPages(pMemNt->Core.pv, pMemNt->apMdls[0]);
+ g_pfnrtMmFreePagesFromMdl(pMemNt->apMdls[0]);
+ ExFreePool(pMemNt->apMdls[0]);
+ }
+ else
+ {
+ if (g_pfnrtExFreePoolWithTag)
+ g_pfnrtExFreePoolWithTag(pMemNt->Core.pv, IPRT_NT_POOL_TAG);
+ else
+ ExFreePool(pMemNt->Core.pv);
+
+ Assert(pMemNt->cMdls == 1 && pMemNt->apMdls[0]);
+ IoFreeMdl(pMemNt->apMdls[0]);
+ }
+ pMemNt->Core.pv = NULL;
+ pMemNt->apMdls[0] = NULL;
+ pMemNt->cMdls = 0;
+ break;
+
+ case RTR0MEMOBJTYPE_CONT:
+ Assert(pMemNt->Core.pv);
+ MmFreeContiguousMemory(pMemNt->Core.pv);
+ pMemNt->Core.pv = NULL;
+
+ Assert(pMemNt->cMdls == 1 && pMemNt->apMdls[0]);
+ IoFreeMdl(pMemNt->apMdls[0]);
+ pMemNt->apMdls[0] = NULL;
+ pMemNt->cMdls = 0;
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ /* rtR0MemObjNativeEnterPhys? */
+ if (!pMemNt->Core.u.Phys.fAllocated)
+ {
+ Assert(!pMemNt->fAllocatedPagesForMdl);
+ /* Nothing to do here. */
+ break;
+ }
+ RT_FALL_THRU();
+
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ if (pMemNt->fAllocatedPagesForMdl)
+ {
+ g_pfnrtMmFreePagesFromMdl(pMemNt->apMdls[0]);
+ ExFreePool(pMemNt->apMdls[0]);
+ pMemNt->apMdls[0] = NULL;
+ pMemNt->cMdls = 0;
+ break;
+ }
+ AssertFailed();
+ break;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ if (pMemNt->pvSecureMem)
+ {
+ g_pfnrtMmUnsecureVirtualMemory(pMemNt->pvSecureMem);
+ pMemNt->pvSecureMem = NULL;
+ }
+ for (uint32_t i = 0; i < pMemNt->cMdls; i++)
+ {
+ MmUnlockPages(pMemNt->apMdls[i]);
+ IoFreeMdl(pMemNt->apMdls[i]);
+ pMemNt->apMdls[i] = NULL;
+ }
+ break;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+/* if (pMemNt->Core.u.ResVirt.R0Process == NIL_RTR0PROCESS)
+ {
+ }
+ else
+ {
+ }*/
+ AssertMsgFailed(("RTR0MEMOBJTYPE_RES_VIRT\n"));
+ return VERR_INTERNAL_ERROR;
+ break;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ {
+ PRTR0MEMOBJNT pMemNtParent = (PRTR0MEMOBJNT)pMemNt->Core.uRel.Child.pParent;
+ Assert(pMemNtParent);
+ Assert(pMemNt->Core.pv);
+ Assert((pMemNt->cMdls == 0 && !pMemNt->fSubMapping) || (pMemNt->cMdls == 1 && pMemNt->fSubMapping));
+ if (pMemNtParent->cMdls)
+ {
+ Assert(pMemNtParent->cMdls == 1 && pMemNtParent->apMdls[0]);
+ Assert( pMemNt->Core.u.Mapping.R0Process == NIL_RTR0PROCESS
+ || pMemNt->Core.u.Mapping.R0Process == RTR0ProcHandleSelf());
+ if (!pMemNt->cMdls)
+ MmUnmapLockedPages(pMemNt->Core.pv, pMemNtParent->apMdls[0]);
+ else
+ {
+ MmUnmapLockedPages(pMemNt->Core.pv, pMemNt->apMdls[0]);
+ IoFreeMdl(pMemNt->apMdls[0]);
+ pMemNt->apMdls[0] = NULL;
+ }
+ }
+ else
+ {
+ Assert( pMemNtParent->Core.enmType == RTR0MEMOBJTYPE_PHYS
+ && !pMemNtParent->Core.u.Phys.fAllocated);
+ Assert(pMemNt->Core.u.Mapping.R0Process == NIL_RTR0PROCESS);
+ Assert(!pMemNt->fSubMapping);
+ MmUnmapIoSpace(pMemNt->Core.pv, pMemNt->Core.cb);
+ }
+ pMemNt->Core.pv = NULL;
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemNt->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ AssertMsgReturn(cb <= _1G, ("%#x\n", cb), VERR_OUT_OF_RANGE); /* for safe size_t -> ULONG */
+ RT_NOREF1(fExecutable);
+
+ /*
+ * Use MmAllocatePagesForMdl if the allocation is a little bit big.
+ */
+ int rc = VERR_NO_PAGE_MEMORY;
+ if ( cb > _1M
+ && g_pfnrtMmAllocatePagesForMdl
+ && g_pfnrtMmFreePagesFromMdl
+ && g_pfnrtMmMapLockedPagesSpecifyCache)
+ {
+ PHYSICAL_ADDRESS Zero;
+ Zero.QuadPart = 0;
+ PHYSICAL_ADDRESS HighAddr;
+ HighAddr.QuadPart = MAXLONGLONG;
+ PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb);
+ if (pMdl)
+ {
+ if (MmGetMdlByteCount(pMdl) >= cb)
+ {
+ __try
+ {
+ void *pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL /* no base address */,
+ FALSE /* no bug check on failure */, NormalPagePriority);
+ if (pv)
+ {
+#ifdef RT_ARCH_AMD64
+ if (fExecutable)
+ MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE);
+#endif
+
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PAGE, pv, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ pMemNt->fAllocatedPagesForMdl = true;
+ pMemNt->cMdls = 1;
+ pMemNt->apMdls[0] = pMdl;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+ MmUnmapLockedPages(pv, pMdl);
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+# ifdef LOG_ENABLED
+ NTSTATUS rcNt = GetExceptionCode();
+ Log(("rtR0MemObjNativeAllocLow: Exception Code %#x\n", rcNt));
+# endif
+ /* nothing */
+ }
+ }
+ g_pfnrtMmFreePagesFromMdl(pMdl);
+ ExFreePool(pMdl);
+ }
+ }
+
+ /*
+ * Try allocate the memory and create an MDL for them so
+ * we can query the physical addresses and do mappings later
+ * without running into out-of-memory conditions and similar problems.
+ */
+ void *pv;
+ if (g_pfnrtExAllocatePoolWithTag)
+ pv = g_pfnrtExAllocatePoolWithTag(NonPagedPool, cb, IPRT_NT_POOL_TAG);
+ else
+ pv = ExAllocatePool(NonPagedPool, cb);
+ if (pv)
+ {
+ PMDL pMdl = IoAllocateMdl(pv, (ULONG)cb, FALSE, FALSE, NULL);
+ if (pMdl)
+ {
+ MmBuildMdlForNonPagedPool(pMdl);
+#ifdef RT_ARCH_AMD64
+ if (fExecutable)
+ MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE);
+#endif
+
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PAGE, pv, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ pMemNt->cMdls = 1;
+ pMemNt->apMdls[0] = pMdl;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ IoFreeMdl(pMdl);
+ }
+ ExFreePool(pv);
+ }
+ return rc;
+}
+
+
+/**
+ * Helper for rtR0MemObjNativeAllocLarge that verifies the result.
+ */
+static bool rtR0MemObjNtVerifyLargePageAlloc(PMDL pMdl, size_t cb, size_t cbLargePage)
+{
+ if (MmGetMdlByteCount(pMdl) >= cb)
+ {
+ PPFN_NUMBER const paPfns = MmGetMdlPfnArray(pMdl);
+ size_t const cPagesPerLargePage = cbLargePage >> PAGE_SHIFT;
+ size_t const cLargePages = cb / cbLargePage;
+ size_t iPage = 0;
+ for (size_t iLargePage = 0; iLargePage < cLargePages; iLargePage++)
+ {
+ PFN_NUMBER Pfn = paPfns[iPage];
+ if (!(Pfn & (cbLargePage >> PAGE_SHIFT) - 1U))
+ {
+ for (size_t iSubPage = 1; iSubPage < cPagesPerLargePage; iSubPage++)
+ {
+ iPage++;
+ Pfn++;
+ if (paPfns[iPage] == Pfn)
+ { /* likely */ }
+ else
+ {
+ Log(("rtR0MemObjNativeAllocLarge: Subpage %#zu in large page #%zu is not contiguous: %#x, expected %#x\n",
+ iSubPage, iLargePage, paPfns[iPage], Pfn));
+ return false;
+ }
+ }
+ }
+ else
+ {
+ Log(("rtR0MemObjNativeAllocLarge: Large page #%zu is misaligned: %#x, cbLargePage=%#zx\n",
+ iLargePage, Pfn, cbLargePage));
+ return false;
+ }
+ }
+ return true;
+ }
+ Log(("rtR0MemObjNativeAllocLarge: Got back too few pages: %#zx, requested %#zx\n", MmGetMdlByteCount(pMdl), cb));
+ return false;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ /*
+ * Need the MmAllocatePagesForMdlEx function so we can specify flags.
+ */
+ if ( g_uRtNtVersion >= RTNT_MAKE_VERSION(6,1) /* Windows 7+ */
+ && g_pfnrtMmAllocatePagesForMdlEx
+ && g_pfnrtMmFreePagesFromMdl
+ && g_pfnrtMmMapLockedPagesSpecifyCache)
+ {
+ ULONG fNtFlags = MM_ALLOCATE_FULLY_REQUIRED /* W7+: Make it fail if we don't get all we ask for.*/
+ | MM_ALLOCATE_REQUIRE_CONTIGUOUS_CHUNKS; /* W7+: The SkipBytes chunks must be physcially contiguous. */
+ if ((fFlags & RTMEMOBJ_ALLOC_LARGE_F_FAST) && g_uRtNtVersion >= RTNT_MAKE_VERSION(6, 2))
+ fNtFlags |= MM_ALLOCATE_FAST_LARGE_PAGES; /* W8+: Don't try too hard, just fail if not enough handy. */
+
+ PHYSICAL_ADDRESS Zero;
+ Zero.QuadPart = 0;
+
+ PHYSICAL_ADDRESS HighAddr;
+ HighAddr.QuadPart = MAXLONGLONG;
+
+ PHYSICAL_ADDRESS Skip;
+ Skip.QuadPart = cbLargePage;
+
+ int rc;
+ PMDL const pMdl = g_pfnrtMmAllocatePagesForMdlEx(Zero, HighAddr, Skip, cb, MmCached, fNtFlags);
+ if (pMdl)
+ {
+ /* Verify the result. */
+ if (rtR0MemObjNtVerifyLargePageAlloc(pMdl, cb, cbLargePage))
+ {
+ /*
+ * Map the allocation into kernel space. Unless the memory is already mapped
+ * somewhere (seems to be actually), I guess it's unlikely that we'll get a
+ * large page aligned mapping back here...
+ */
+ __try
+ {
+ void *pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL /* no base address */,
+ FALSE /* no bug check on failure */, NormalPagePriority);
+ if (pv)
+ {
+ /*
+ * Create the memory object.
+ */
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PAGE, pv, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ pMemNt->fAllocatedPagesForMdl = true;
+ pMemNt->cMdls = 1;
+ pMemNt->apMdls[0] = pMdl;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+
+ MmUnmapLockedPages(pv, pMdl);
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+#ifdef LOG_ENABLED
+ NTSTATUS rcNt = GetExceptionCode();
+ Log(("rtR0MemObjNativeAllocLarge: Exception Code %#x\n", rcNt));
+#endif
+ /* nothing */
+ }
+ }
+
+ g_pfnrtMmFreePagesFromMdl(pMdl);
+ ExFreePool(pMdl);
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = fFlags & RTMEMOBJ_ALLOC_LARGE_F_FAST ? VERR_TRY_AGAIN : VERR_NO_MEMORY;
+ return rc;
+ }
+
+ return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ AssertMsgReturn(cb <= _1G, ("%#x\n", cb), VERR_OUT_OF_RANGE); /* for safe size_t -> ULONG */
+
+ /*
+ * Try see if we get lucky first...
+ * (We could probably just assume we're lucky on NT4.)
+ */
+ int rc = rtR0MemObjNativeAllocPage(ppMem, cb, fExecutable, pszTag);
+ if (RT_SUCCESS(rc))
+ {
+ size_t iPage = cb >> PAGE_SHIFT;
+ while (iPage-- > 0)
+ if (rtR0MemObjNativeGetPagePhysAddr(*ppMem, iPage) >= _4G)
+ {
+ rc = VERR_NO_LOW_MEMORY;
+ break;
+ }
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* The following ASSUMES that rtR0MemObjNativeAllocPage returns a completed object. */
+ RTR0MemObjFree(*ppMem, false);
+ *ppMem = NULL;
+ }
+
+ /*
+ * Use MmAllocatePagesForMdl to specify the range of physical addresses we wish to use.
+ */
+ if ( g_pfnrtMmAllocatePagesForMdl
+ && g_pfnrtMmFreePagesFromMdl
+ && g_pfnrtMmMapLockedPagesSpecifyCache)
+ {
+ PHYSICAL_ADDRESS Zero;
+ Zero.QuadPart = 0;
+ PHYSICAL_ADDRESS HighAddr;
+ HighAddr.QuadPart = _4G - 1;
+ PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb);
+ if (pMdl)
+ {
+ if (MmGetMdlByteCount(pMdl) >= cb)
+ {
+ __try
+ {
+ void *pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl, KernelMode, MmCached, NULL /* no base address */,
+ FALSE /* no bug check on failure */, NormalPagePriority);
+ if (pv)
+ {
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_LOW, pv, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ pMemNt->fAllocatedPagesForMdl = true;
+ pMemNt->cMdls = 1;
+ pMemNt->apMdls[0] = pMdl;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+ MmUnmapLockedPages(pv, pMdl);
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+# ifdef LOG_ENABLED
+ NTSTATUS rcNt = GetExceptionCode();
+ Log(("rtR0MemObjNativeAllocLow: Exception Code %#x\n", rcNt));
+# endif
+ /* nothing */
+ }
+ }
+ g_pfnrtMmFreePagesFromMdl(pMdl);
+ ExFreePool(pMdl);
+ }
+ }
+
+ /*
+ * Fall back on contiguous memory...
+ */
+ return rtR0MemObjNativeAllocCont(ppMem, cb, fExecutable, pszTag);
+}
+
+
+/**
+ * Internal worker for rtR0MemObjNativeAllocCont(), rtR0MemObjNativeAllocPhys()
+ * and rtR0MemObjNativeAllocPhysNC() that takes a max physical address in addition
+ * to what rtR0MemObjNativeAllocCont() does.
+ *
+ * @returns IPRT status code.
+ * @param ppMem Where to store the pointer to the ring-0 memory object.
+ * @param cb The size.
+ * @param fExecutable Whether the mapping should be executable or not.
+ * @param PhysHighest The highest physical address for the pages in allocation.
+ * @param uAlignment The alignment of the physical memory to allocate.
+ * Supported values are PAGE_SIZE, _2M, _4M and _1G.
+ * @param pszTag Allocation tag used for statistics and such.
+ */
+static int rtR0MemObjNativeAllocContEx(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, RTHCPHYS PhysHighest,
+ size_t uAlignment, const char *pszTag)
+{
+ AssertMsgReturn(cb <= _1G, ("%#x\n", cb), VERR_OUT_OF_RANGE); /* for safe size_t -> ULONG */
+ RT_NOREF1(fExecutable);
+
+ /*
+ * Allocate the memory and create an MDL for it.
+ */
+ PHYSICAL_ADDRESS PhysAddrHighest;
+ PhysAddrHighest.QuadPart = PhysHighest;
+ void *pv;
+ if (g_pfnrtMmAllocateContiguousMemorySpecifyCache)
+ {
+ PHYSICAL_ADDRESS PhysAddrLowest, PhysAddrBoundary;
+ PhysAddrLowest.QuadPart = 0;
+ PhysAddrBoundary.QuadPart = (uAlignment == PAGE_SIZE) ? 0 : uAlignment;
+ pv = g_pfnrtMmAllocateContiguousMemorySpecifyCache(cb, PhysAddrLowest, PhysAddrHighest, PhysAddrBoundary, MmCached);
+ }
+ else if (uAlignment == PAGE_SIZE)
+ pv = MmAllocateContiguousMemory(cb, PhysAddrHighest);
+ else
+ return VERR_NOT_SUPPORTED;
+ if (!pv)
+ return VERR_NO_MEMORY;
+
+ PMDL pMdl = IoAllocateMdl(pv, (ULONG)cb, FALSE, FALSE, NULL);
+ if (pMdl)
+ {
+ MmBuildMdlForNonPagedPool(pMdl);
+#ifdef RT_ARCH_AMD64
+ if (fExecutable)
+ MmProtectMdlSystemAddress(pMdl, PAGE_EXECUTE_READWRITE);
+#endif
+
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_CONT, pv, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ pMemNt->Core.u.Cont.Phys = (RTHCPHYS)*MmGetMdlPfnArray(pMdl) << PAGE_SHIFT;
+ pMemNt->cMdls = 1;
+ pMemNt->apMdls[0] = pMdl;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+
+ IoFreeMdl(pMdl);
+ }
+ MmFreeContiguousMemory(pv);
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ return rtR0MemObjNativeAllocContEx(ppMem, cb, fExecutable, _4G-1, PAGE_SIZE /* alignment */, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment,
+ const char *pszTag)
+{
+ /*
+ * Try and see if we're lucky and get a contiguous chunk from MmAllocatePagesForMdl.
+ *
+ * This is preferable to using MmAllocateContiguousMemory because there are
+ * a few situations where the memory shouldn't be mapped, like for instance
+ * VT-x control memory. Since these are rather small allocations (one or
+ * two pages) MmAllocatePagesForMdl will probably be able to satisfy the
+ * request.
+ *
+ * If the allocation is big, the chances are *probably* not very good. The
+ * current limit is kind of random...
+ */
+ if ( cb < _128K
+ && uAlignment == PAGE_SIZE
+ && g_pfnrtMmAllocatePagesForMdl
+ && g_pfnrtMmFreePagesFromMdl)
+ {
+ PHYSICAL_ADDRESS Zero;
+ Zero.QuadPart = 0;
+ PHYSICAL_ADDRESS HighAddr;
+ HighAddr.QuadPart = PhysHighest == NIL_RTHCPHYS ? MAXLONGLONG : PhysHighest;
+ PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb);
+ if (pMdl)
+ {
+ if (MmGetMdlByteCount(pMdl) >= cb)
+ {
+ PPFN_NUMBER paPfns = MmGetMdlPfnArray(pMdl);
+ PFN_NUMBER Pfn = paPfns[0] + 1;
+ const size_t cPages = cb >> PAGE_SHIFT;
+ size_t iPage;
+ for (iPage = 1; iPage < cPages; iPage++, Pfn++)
+ if (paPfns[iPage] != Pfn)
+ break;
+ if (iPage >= cPages)
+ {
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ pMemNt->Core.u.Phys.fAllocated = true;
+ pMemNt->Core.u.Phys.PhysBase = (RTHCPHYS)paPfns[0] << PAGE_SHIFT;
+ pMemNt->fAllocatedPagesForMdl = true;
+ pMemNt->cMdls = 1;
+ pMemNt->apMdls[0] = pMdl;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ g_pfnrtMmFreePagesFromMdl(pMdl);
+ ExFreePool(pMdl);
+ }
+ }
+
+ return rtR0MemObjNativeAllocContEx(ppMem, cb, false, PhysHighest, uAlignment, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ if (g_pfnrtMmAllocatePagesForMdl && g_pfnrtMmFreePagesFromMdl)
+ {
+ /** @todo use the Ex version with the fail-if-not-all-requested-pages flag
+ * when possible. */
+ PHYSICAL_ADDRESS Zero;
+ Zero.QuadPart = 0;
+ PHYSICAL_ADDRESS HighAddr;
+ HighAddr.QuadPart = PhysHighest == NIL_RTHCPHYS ? MAXLONGLONG : PhysHighest;
+ PMDL pMdl = g_pfnrtMmAllocatePagesForMdl(Zero, HighAddr, Zero, cb);
+ if (pMdl)
+ {
+ if (MmGetMdlByteCount(pMdl) >= cb)
+ {
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PHYS_NC, NULL, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ pMemNt->fAllocatedPagesForMdl = true;
+ pMemNt->cMdls = 1;
+ pMemNt->apMdls[0] = pMdl;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+ }
+ g_pfnrtMmFreePagesFromMdl(pMdl);
+ ExFreePool(pMdl);
+ }
+ return VERR_NO_MEMORY;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy,
+ const char *pszTag)
+{
+ AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE || uCachePolicy == RTMEM_CACHE_POLICY_MMIO, VERR_NOT_SUPPORTED);
+
+ /*
+ * Validate the address range and create a descriptor for it.
+ */
+ PFN_NUMBER Pfn = (PFN_NUMBER)(Phys >> PAGE_SHIFT);
+ if (((RTHCPHYS)Pfn << PAGE_SHIFT) != Phys)
+ return VERR_ADDRESS_TOO_BIG;
+
+ /*
+ * Create the IPRT memory object.
+ */
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.u.Phys.PhysBase = Phys;
+ pMemNt->Core.u.Phys.fAllocated = false;
+ pMemNt->Core.u.Phys.uCachePolicy = uCachePolicy;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Internal worker for locking down pages.
+ *
+ * @return IPRT status code.
+ *
+ * @param ppMem Where to store the memory object pointer.
+ * @param pv First page.
+ * @param cb Number of bytes.
+ * @param fAccess The desired access, a combination of RTMEM_PROT_READ
+ * and RTMEM_PROT_WRITE.
+ * @param R0Process The process \a pv and \a cb refers to.
+ * @param pszTag Allocation tag used for statistics and such.
+ */
+static int rtR0MemObjNtLock(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, RTR0PROCESS R0Process,
+ const char *pszTag)
+{
+ /*
+ * Calc the number of MDLs we need and allocate the memory object structure.
+ */
+ size_t cMdls = cb / MAX_LOCK_MEM_SIZE;
+ if (cb % MAX_LOCK_MEM_SIZE)
+ cMdls++;
+ if (cMdls >= UINT32_MAX)
+ return VERR_OUT_OF_RANGE;
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJNT, apMdls[cMdls]),
+ RTR0MEMOBJTYPE_LOCK, pv, cb, pszTag);
+ if (!pMemNt)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Loop locking down the sub parts of the memory.
+ */
+ int rc = VINF_SUCCESS;
+ size_t cbTotal = 0;
+ uint8_t *pb = (uint8_t *)pv;
+ uint32_t iMdl;
+ for (iMdl = 0; iMdl < cMdls; iMdl++)
+ {
+ /*
+ * Calc the Mdl size and allocate it.
+ */
+ size_t cbCur = cb - cbTotal;
+ if (cbCur > MAX_LOCK_MEM_SIZE)
+ cbCur = MAX_LOCK_MEM_SIZE;
+ AssertMsg(cbCur, ("cbCur: 0!\n"));
+ PMDL pMdl = IoAllocateMdl(pb, (ULONG)cbCur, FALSE, FALSE, NULL);
+ if (!pMdl)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ /*
+ * Lock the pages.
+ */
+ __try
+ {
+ MmProbeAndLockPages(pMdl,
+ R0Process == NIL_RTR0PROCESS ? KernelMode : UserMode,
+ fAccess == RTMEM_PROT_READ
+ ? IoReadAccess
+ : fAccess == RTMEM_PROT_WRITE
+ ? IoWriteAccess
+ : IoModifyAccess);
+
+ pMemNt->apMdls[iMdl] = pMdl;
+ pMemNt->cMdls++;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ IoFreeMdl(pMdl);
+ rc = VERR_LOCK_FAILED;
+ break;
+ }
+
+ if ( R0Process != NIL_RTR0PROCESS
+ && g_pfnrtMmSecureVirtualMemory
+ && g_pfnrtMmUnsecureVirtualMemory)
+ {
+ /* Make sure the user process can't change the allocation. */
+ pMemNt->pvSecureMem = g_pfnrtMmSecureVirtualMemory(pv, cb,
+ fAccess & RTMEM_PROT_WRITE
+ ? PAGE_READWRITE
+ : PAGE_READONLY);
+ if (!pMemNt->pvSecureMem)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ }
+
+ /* next */
+ cbTotal += cbCur;
+ pb += cbCur;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pMemNt->cMdls == cMdls);
+ pMemNt->Core.u.Lock.R0Process = R0Process;
+ *ppMem = &pMemNt->Core;
+ return rc;
+ }
+
+ /*
+ * We failed, perform cleanups.
+ */
+ while (iMdl-- > 0)
+ {
+ MmUnlockPages(pMemNt->apMdls[iMdl]);
+ IoFreeMdl(pMemNt->apMdls[iMdl]);
+ pMemNt->apMdls[iMdl] = NULL;
+ }
+ if (pMemNt->pvSecureMem)
+ {
+ if (g_pfnrtMmUnsecureVirtualMemory)
+ g_pfnrtMmUnsecureVirtualMemory(pMemNt->pvSecureMem);
+ pMemNt->pvSecureMem = NULL;
+ }
+
+ rtR0MemObjDelete(&pMemNt->Core);
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED);
+ /* (Can use MmProbeAndLockProcessPages if we need to mess with other processes later.) */
+ return rtR0MemObjNtLock(ppMem, (void *)R3Ptr, cb, fAccess, R0Process, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ return rtR0MemObjNtLock(ppMem, pv, cb, fAccess, NIL_RTR0PROCESS, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ const char *pszTag)
+{
+ /*
+ * MmCreateSection(SEC_RESERVE) + MmMapViewInSystemSpace perhaps?
+ * Or MmAllocateMappingAddress?
+ */
+ RT_NOREF(ppMem, pvFixed, cb, uAlignment, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ /*
+ * ZeCreateSection(SEC_RESERVE) + ZwMapViewOfSection perhaps?
+ */
+ RT_NOREF(ppMem, R3PtrFixed, cb, uAlignment, R0Process, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * Internal worker for rtR0MemObjNativeMapKernel and rtR0MemObjNativeMapUser.
+ *
+ * @returns IPRT status code.
+ * @param ppMem Where to store the memory object for the mapping.
+ * @param pMemToMap The memory object to map.
+ * @param pvFixed Where to map it. (void *)-1 if anywhere is fine.
+ * @param uAlignment The alignment requirement for the mapping.
+ * @param fProt The desired page protection for the mapping.
+ * @param R0Process If NIL_RTR0PROCESS map into system (kernel) memory.
+ * If not nil, it's the current process.
+ * @param offSub Offset into @a pMemToMap to start mapping.
+ * @param cbSub The number of bytes to map from @a pMapToMem. 0 if
+ * we're to map everything. Non-zero if @a offSub is
+ * non-zero.
+ * @param pszTag Allocation tag used for statistics and such.
+ */
+static int rtR0MemObjNtMap(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ int rc = VERR_MAP_FAILED;
+
+ /*
+ * Check that the specified alignment is supported.
+ */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * There are two basic cases here, either we've got an MDL and can
+ * map it using MmMapLockedPages, or we've got a contiguous physical
+ * range (MMIO most likely) and can use MmMapIoSpace.
+ */
+ PRTR0MEMOBJNT pMemNtToMap = (PRTR0MEMOBJNT)pMemToMap;
+ if (pMemNtToMap->cMdls)
+ {
+ /* don't attempt map locked regions with more than one mdl. */
+ if (pMemNtToMap->cMdls != 1)
+ return VERR_NOT_SUPPORTED;
+
+ /* Need g_pfnrtMmMapLockedPagesSpecifyCache to map to a specific address. */
+ if (pvFixed != (void *)-1 && g_pfnrtMmMapLockedPagesSpecifyCache == NULL)
+ return VERR_NOT_SUPPORTED;
+
+ /* we can't map anything to the first page, sorry. */
+ if (pvFixed == 0)
+ return VERR_NOT_SUPPORTED;
+
+ /* only one system mapping for now - no time to figure out MDL restrictions right now. */
+ if ( pMemNtToMap->Core.uRel.Parent.cMappings
+ && R0Process == NIL_RTR0PROCESS)
+ {
+ if (pMemNtToMap->Core.enmType != RTR0MEMOBJTYPE_PHYS_NC)
+ return VERR_NOT_SUPPORTED;
+ uint32_t iMapping = pMemNtToMap->Core.uRel.Parent.cMappings;
+ while (iMapping-- > 0)
+ {
+ PRTR0MEMOBJNT pMapping = (PRTR0MEMOBJNT)pMemNtToMap->Core.uRel.Parent.papMappings[iMapping];
+ if ( pMapping->Core.enmType != RTR0MEMOBJTYPE_MAPPING
+ || pMapping->Core.u.Mapping.R0Process == NIL_RTR0PROCESS)
+ return VERR_NOT_SUPPORTED;
+ }
+ }
+
+ /* Create a partial MDL if this is a sub-range request. */
+ PMDL pMdl;
+ if (!offSub && !cbSub)
+ pMdl = pMemNtToMap->apMdls[0];
+ else
+ {
+ pMdl = IoAllocateMdl(NULL, (ULONG)cbSub, FALSE, FALSE, NULL);
+ if (pMdl)
+ IoBuildPartialMdl(pMemNtToMap->apMdls[0], pMdl,
+ (uint8_t *)MmGetMdlVirtualAddress(pMemNtToMap->apMdls[0]) + offSub, (ULONG)cbSub);
+ else
+ {
+ IoFreeMdl(pMdl);
+ return VERR_NO_MEMORY;
+ }
+ }
+
+ __try
+ {
+ /** @todo uAlignment */
+ /** @todo How to set the protection on the pages? */
+ void *pv;
+ if (g_pfnrtMmMapLockedPagesSpecifyCache)
+ pv = g_pfnrtMmMapLockedPagesSpecifyCache(pMdl,
+ R0Process == NIL_RTR0PROCESS ? KernelMode : UserMode,
+ MmCached,
+ pvFixed != (void *)-1 ? pvFixed : NULL,
+ FALSE /* no bug check on failure */,
+ NormalPagePriority);
+ else
+ pv = MmMapLockedPages(pMdl, R0Process == NIL_RTR0PROCESS ? KernelMode : UserMode);
+ if (pv)
+ {
+ NOREF(fProt);
+
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew( !offSub && !cbSub
+ ? sizeof(*pMemNt) : RT_UOFFSETOF_DYN(RTR0MEMOBJNT, apMdls[1]),
+ RTR0MEMOBJTYPE_MAPPING, pv, pMemNtToMap->Core.cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.u.Mapping.R0Process = R0Process;
+ if (!offSub && !cbSub)
+ pMemNt->fSubMapping = false;
+ else
+ {
+ pMemNt->apMdls[0] = pMdl;
+ pMemNt->cMdls = 1;
+ pMemNt->fSubMapping = true;
+ }
+
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ MmUnmapLockedPages(pv, pMdl);
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+#ifdef LOG_ENABLED
+ NTSTATUS rcNt = GetExceptionCode();
+ Log(("rtR0MemObjNtMap: Exception Code %#x\n", rcNt));
+#endif
+
+ /* nothing */
+ rc = VERR_MAP_FAILED;
+ }
+
+ }
+ else
+ {
+ AssertReturn( pMemNtToMap->Core.enmType == RTR0MEMOBJTYPE_PHYS
+ && !pMemNtToMap->Core.u.Phys.fAllocated, VERR_INTERNAL_ERROR);
+
+ /* cannot map phys mem to user space (yet). */
+ if (R0Process != NIL_RTR0PROCESS)
+ return VERR_NOT_SUPPORTED;
+
+ /* Cannot sub-mak these (yet). */
+ AssertMsgReturn(!offSub && !cbSub, ("%#zx %#zx\n", offSub, cbSub), VERR_NOT_SUPPORTED);
+
+
+ /** @todo uAlignment */
+ /** @todo How to set the protection on the pages? */
+ PHYSICAL_ADDRESS Phys;
+ Phys.QuadPart = pMemNtToMap->Core.u.Phys.PhysBase;
+ void *pv = MmMapIoSpace(Phys, pMemNtToMap->Core.cb,
+ pMemNtToMap->Core.u.Phys.uCachePolicy == RTMEM_CACHE_POLICY_MMIO ? MmNonCached : MmCached);
+ if (pv)
+ {
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)rtR0MemObjNew(sizeof(*pMemNt), RTR0MEMOBJTYPE_MAPPING, pv,
+ pMemNtToMap->Core.cb, pszTag);
+ if (pMemNt)
+ {
+ pMemNt->Core.u.Mapping.R0Process = R0Process;
+ *ppMem = &pMemNt->Core;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ MmUnmapIoSpace(pv, pMemNtToMap->Core.cb);
+ }
+ }
+
+ NOREF(uAlignment); NOREF(fProt);
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ return rtR0MemObjNtMap(ppMem, pMemToMap, pvFixed, uAlignment, fProt, NIL_RTR0PROCESS, offSub, cbSub, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ AssertReturn(R0Process == RTR0ProcHandleSelf(), VERR_NOT_SUPPORTED);
+ return rtR0MemObjNtMap(ppMem, pMemToMap, (void *)R3PtrFixed, uAlignment, fProt, R0Process, offSub, cbSub, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+#if 0
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)pMem;
+#endif
+
+ /*
+ * Seems there are some issues with this MmProtectMdlSystemAddress API, so
+ * this code isn't currently enabled until we've tested it with the verifier.
+ */
+#if 0
+ /*
+ * The API we've got requires a kernel mapping.
+ */
+ if ( pMemNt->cMdls
+ && g_pfnrtMmProtectMdlSystemAddress
+ && (g_uRtNtMajorVer > 6 || (g_uRtNtMajorVer == 6 && g_uRtNtMinorVer >= 1)) /* Windows 7 and later. */
+ && pMemNt->Core.pv != NULL
+ && ( pMemNt->Core.enmType == RTR0MEMOBJTYPE_PAGE
+ || pMemNt->Core.enmType == RTR0MEMOBJTYPE_LOW
+ || pMemNt->Core.enmType == RTR0MEMOBJTYPE_CONT
+ || ( pMemNt->Core.enmType == RTR0MEMOBJTYPE_LOCK
+ && pMemNt->Core.u.Lock.R0Process == NIL_RTPROCESS)
+ || ( pMemNt->Core.enmType == RTR0MEMOBJTYPE_MAPPING
+ && pMemNt->Core.u.Mapping.R0Process == NIL_RTPROCESS) ) )
+ {
+ /* Convert the protection. */
+ LOCK_OPERATION enmLockOp;
+ ULONG fAccess;
+ switch (fProt)
+ {
+ case RTMEM_PROT_NONE:
+ fAccess = PAGE_NOACCESS;
+ enmLockOp = IoReadAccess;
+ break;
+ case RTMEM_PROT_READ:
+ fAccess = PAGE_READONLY;
+ enmLockOp = IoReadAccess;
+ break;
+ case RTMEM_PROT_WRITE:
+ case RTMEM_PROT_WRITE | RTMEM_PROT_READ:
+ fAccess = PAGE_READWRITE;
+ enmLockOp = IoModifyAccess;
+ break;
+ case RTMEM_PROT_EXEC:
+ fAccess = PAGE_EXECUTE;
+ enmLockOp = IoReadAccess;
+ break;
+ case RTMEM_PROT_EXEC | RTMEM_PROT_READ:
+ fAccess = PAGE_EXECUTE_READ;
+ enmLockOp = IoReadAccess;
+ break;
+ case RTMEM_PROT_EXEC | RTMEM_PROT_WRITE:
+ case RTMEM_PROT_EXEC | RTMEM_PROT_WRITE | RTMEM_PROT_READ:
+ fAccess = PAGE_EXECUTE_READWRITE;
+ enmLockOp = IoModifyAccess;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_FLAGS);
+ }
+
+ NTSTATUS rcNt = STATUS_SUCCESS;
+# if 0 /** @todo test this against the verifier. */
+ if (offSub == 0 && pMemNt->Core.cb == cbSub)
+ {
+ uint32_t iMdl = pMemNt->cMdls;
+ while (iMdl-- > 0)
+ {
+ rcNt = g_pfnrtMmProtectMdlSystemAddress(pMemNt->apMdls[i], fAccess);
+ if (!NT_SUCCESS(rcNt))
+ break;
+ }
+ }
+ else
+# endif
+ {
+ /*
+ * We ASSUME the following here:
+ * - MmProtectMdlSystemAddress can deal with nonpaged pool memory
+ * - MmProtectMdlSystemAddress doesn't actually store anything in the MDL we pass it.
+ * - We are not required to call MmProtectMdlSystemAddress with PAGE_READWRITE for the
+ * exact same ranges prior to freeing them.
+ *
+ * So, we lock the pages temporarily, call the API and unlock them.
+ */
+ uint8_t *pbCur = (uint8_t *)pMemNt->Core.pv + offSub;
+ while (cbSub > 0 && NT_SUCCESS(rcNt))
+ {
+ size_t cbCur = cbSub;
+ if (cbCur > MAX_LOCK_MEM_SIZE)
+ cbCur = MAX_LOCK_MEM_SIZE;
+ PMDL pMdl = IoAllocateMdl(pbCur, (ULONG)cbCur, FALSE, FALSE, NULL);
+ if (pMdl)
+ {
+ __try
+ {
+ MmProbeAndLockPages(pMdl, KernelMode, enmLockOp);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ rcNt = GetExceptionCode();
+ }
+ if (NT_SUCCESS(rcNt))
+ {
+ rcNt = g_pfnrtMmProtectMdlSystemAddress(pMdl, fAccess);
+ MmUnlockPages(pMdl);
+ }
+ IoFreeMdl(pMdl);
+ }
+ else
+ rcNt = STATUS_NO_MEMORY;
+ pbCur += cbCur;
+ cbSub -= cbCur;
+ }
+ }
+
+ if (NT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+#else
+ RT_NOREF4(pMem, offSub, cbSub, fProt);
+#endif
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ PRTR0MEMOBJNT pMemNt = (PRTR0MEMOBJNT)pMem;
+
+ if (pMemNt->cMdls)
+ {
+ if (pMemNt->cMdls == 1)
+ {
+ PPFN_NUMBER paPfns = MmGetMdlPfnArray(pMemNt->apMdls[0]);
+ return (RTHCPHYS)paPfns[iPage] << PAGE_SHIFT;
+ }
+
+ size_t iMdl = iPage / (MAX_LOCK_MEM_SIZE >> PAGE_SHIFT);
+ size_t iMdlPfn = iPage % (MAX_LOCK_MEM_SIZE >> PAGE_SHIFT);
+ PPFN_NUMBER paPfns = MmGetMdlPfnArray(pMemNt->apMdls[iMdl]);
+ return (RTHCPHYS)paPfns[iMdlPfn] << PAGE_SHIFT;
+ }
+
+ switch (pMemNt->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_MAPPING:
+ return rtR0MemObjNativeGetPagePhysAddr(pMemNt->Core.uRel.Child.pParent, iPage);
+
+ case RTR0MEMOBJTYPE_PHYS:
+ return pMemNt->Core.u.Phys.PhysBase + (iPage << PAGE_SHIFT);
+
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ case RTR0MEMOBJTYPE_LOCK:
+ default:
+ AssertMsgFailed(("%d\n", pMemNt->Core.enmType));
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ return NIL_RTHCPHYS;
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp
new file mode 100644
index 00000000..79df9d61
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/memuserkernel-r0drv-nt.cpp
@@ -0,0 +1,210 @@
+/* $Id: memuserkernel-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - User & Kernel Memory, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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-nt-kernel.h"
+
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+
+#include "internal-r0drv-nt.h"
+
+
+RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb)
+{
+ __try
+ {
+ ProbeForRead((PVOID)R3PtrSrc, cb, 1);
+ memcpy(pvDst, (void const *)R3PtrSrc, cb);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ return VERR_ACCESS_DENIED;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb)
+{
+ __try
+ {
+ ProbeForWrite((PVOID)R3PtrDst, cb, 1);
+ memcpy((void *)R3PtrDst, pvSrc, cb);
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ return VERR_ACCESS_DENIED;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr)
+{
+#ifdef IPRT_TARGET_NT4
+ uintptr_t const uLast = g_puRtMmHighestUserAddress ? *g_puRtMmHighestUserAddress : ~(uintptr_t)0 / 2;
+#else
+ uintptr_t const uLast = (uintptr_t)MM_HIGHEST_USER_ADDRESS;
+#endif
+ return R3Ptr <= uLast;
+}
+
+
+RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv)
+{
+#ifdef IPRT_TARGET_NT4
+ uintptr_t const uFirst = g_puRtMmSystemRangeStart ? *g_puRtMmSystemRangeStart : ~(uintptr_t)0 / 2 + 1;
+#else
+ uintptr_t const uFirst = (uintptr_t)MM_SYSTEM_RANGE_START;
+#endif
+ return (uintptr_t)pv >= uFirst;
+}
+
+
+RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void)
+{
+ return true;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb)
+{
+ if (!RTR0MemKernelIsValidAddr((void *)pvSrc))
+ return VERR_ACCESS_DENIED;
+
+ uint8_t *pbDst = (uint8_t *)pvDst;
+ uint8_t const *pbSrc = (uint8_t const *)pvSrc;
+
+#if 0
+ /*
+ * The try+except stuff does not work for kernel addresses.
+ */
+ __try
+ {
+ while (cb-- > 0)
+ *pbDst++ = *pbSrc++;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ return VERR_ACCESS_DENIED;
+ }
+#else
+ /*
+ * This is the best I can come up with for now: Work page-by-page using MmIsAddressValid.
+ */
+ while (cb > 0)
+ {
+ if (!MmIsAddressValid((PVOID)pbSrc))
+ return VERR_ACCESS_DENIED;
+
+ size_t cbToCopy = (uintptr_t)pbSrc & PAGE_OFFSET_MASK;
+ if (cbToCopy > cb)
+ cbToCopy = cb;
+ cb -= cbToCopy;
+
+ __try /* doesn't work, but can't hurt, right? */
+ {
+ while (cbToCopy-- > 0)
+ *pbDst++ = *pbSrc++;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ return VERR_ACCESS_DENIED;
+ }
+ }
+#endif
+ return VINF_SUCCESS;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb)
+{
+ if (!RTR0MemKernelIsValidAddr(pvDst))
+ return VERR_ACCESS_DENIED;
+#if 0
+ uint8_t *pbDst = (uint8_t *)pvDst;
+ uint8_t const *pbSrc = (uint8_t const *)pvSrc;
+# if 0
+ /*
+ * The try+except stuff does not work for kernel addresses.
+ */
+ __try
+ {
+ while (cb-- > 0)
+ *pbDst++ = *pbSrc++;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ return VERR_ACCESS_DENIED;
+ }
+
+# else
+ /*
+ * This is the best I can come up with for now: Work page-by-page using MmIsAddressValid.
+ * Note! MmIsAddressValid does not indicate that it's writable, so we're a bit buggered if it isn't...
+ */
+ while (cb > 0)
+ {
+ if (!MmIsAddressValid((PVOID)pbSrc))
+ return VERR_ACCESS_DENIED;
+
+ size_t cbToCopy = (uintptr_t)pbSrc & PAGE_OFFSET_MASK;
+ if (cbToCopy > cb)
+ cbToCopy = cb;
+ cb -= cbToCopy;
+
+ __try /* doesn't work, but can't hurt, right? */
+ {
+ while (cbToCopy-- > 0)
+ *pbDst++ = *pbSrc++;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ return VERR_ACCESS_DENIED;
+ }
+ }
+# endif
+ return VINF_SUCCESS;
+#else
+ RT_NOREF(pvDst, pvSrc, cb);
+ return VERR_NOT_SUPPORTED;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp
new file mode 100644
index 00000000..a2b27be2
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/mp-r0drv-nt.cpp
@@ -0,0 +1,1996 @@
+/* $Id: mp-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/time.h>
+#include "r0drv/mp-r0drv.h"
+#include "symdb.h"
+#include "internal-r0drv-nt.h"
+#include "internal/mp.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef enum
+{
+ RT_NT_CPUID_SPECIFIC,
+ RT_NT_CPUID_PAIR,
+ RT_NT_CPUID_OTHERS,
+ RT_NT_CPUID_ALL
+} RT_NT_CPUID;
+
+
+/**
+ * Used by the RTMpOnSpecific.
+ */
+typedef struct RTMPNTONSPECIFICARGS
+{
+ /** Set if we're executing. */
+ bool volatile fExecuting;
+ /** Set when done executing. */
+ bool volatile fDone;
+ /** Number of references to this heap block. */
+ uint32_t volatile cRefs;
+ /** Event that the calling thread is waiting on. */
+ KEVENT DoneEvt;
+ /** The deferred procedure call object. */
+ KDPC Dpc;
+ /** The callback argument package. */
+ RTMPARGS CallbackArgs;
+} RTMPNTONSPECIFICARGS;
+/** Pointer to an argument/state structure for RTMpOnSpecific on NT. */
+typedef RTMPNTONSPECIFICARGS *PRTMPNTONSPECIFICARGS;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Inactive bit for g_aidRtMpNtByCpuSetIdx. */
+#define RTMPNT_ID_F_INACTIVE RT_BIT_32(31)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Maximum number of processor groups. */
+uint32_t g_cRtMpNtMaxGroups;
+/** Maximum number of processors. */
+uint32_t g_cRtMpNtMaxCpus;
+/** Number of active processors. */
+uint32_t volatile g_cRtMpNtActiveCpus;
+/** The NT CPU set.
+ * KeQueryActiveProcssors() cannot be called at all IRQLs and therefore we'll
+ * have to cache it. Fortunately, NT doesn't really support taking CPUs offline,
+ * and taking them online was introduced with W2K8 where it is intended for virtual
+ * machines and not real HW. We update this, g_cRtMpNtActiveCpus and
+ * g_aidRtMpNtByCpuSetIdx from the rtR0NtMpProcessorChangeCallback.
+ */
+RTCPUSET g_rtMpNtCpuSet;
+
+/** Static per group info.
+ * @remarks With 256 groups this takes up 33KB. */
+static struct
+{
+ /** The max CPUs in the group. */
+ uint16_t cMaxCpus;
+ /** The number of active CPUs at the time of initialization. */
+ uint16_t cActiveCpus;
+ /** CPU set indexes for each CPU in the group. */
+ int16_t aidxCpuSetMembers[64];
+} g_aRtMpNtCpuGroups[256];
+/** Maps CPU set indexes to RTCPUID.
+ * Inactive CPUs has bit 31 set (RTMPNT_ID_F_INACTIVE) so we can identify them
+ * and shuffle duplicates during CPU hotplugging. We assign temporary IDs to
+ * the inactive CPUs starting at g_cRtMpNtMaxCpus - 1, ASSUMING that active
+ * CPUs has IDs from 0 to g_cRtMpNtActiveCpus. */
+RTCPUID g_aidRtMpNtByCpuSetIdx[RTCPUSET_MAX_CPUS];
+/** The handle of the rtR0NtMpProcessorChangeCallback registration. */
+static PVOID g_pvMpCpuChangeCallback = NULL;
+/** Size of the KAFFINITY_EX structure.
+ * This increased from 20 to 32 bitmap words in the 2020 H2 windows 10 release
+ * (i.e. 1280 to 2048 CPUs). We expect this to increase in the future. */
+static size_t g_cbRtMpNtKaffinityEx = RT_UOFFSETOF(KAFFINITY_EX, Bitmap)
+ + RT_SIZEOFMEMB(KAFFINITY_EX, Bitmap[0]) * 256;
+/** The size value of the KAFFINITY_EX structure. */
+static uint16_t g_cRtMpNtKaffinityExEntries = 256;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static VOID __stdcall rtR0NtMpProcessorChangeCallback(void *pvUser, PKE_PROCESSOR_CHANGE_NOTIFY_CONTEXT pChangeCtx,
+ PNTSTATUS prcOperationStatus);
+static int rtR0NtInitQueryGroupRelations(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX **ppInfo);
+
+
+
+/**
+ * Initalizes multiprocessor globals (called by rtR0InitNative).
+ *
+ * @returns IPRT status code.
+ * @param pOsVerInfo Version information.
+ */
+DECLHIDDEN(int) rtR0MpNtInit(RTNTSDBOSVER const *pOsVerInfo)
+{
+#define MY_CHECK_BREAK(a_Check, a_DbgPrintArgs) \
+ AssertMsgBreakStmt(a_Check, a_DbgPrintArgs, DbgPrint a_DbgPrintArgs; rc = VERR_INTERNAL_ERROR_4 )
+#define MY_CHECK_RETURN(a_Check, a_DbgPrintArgs, a_rcRet) \
+ AssertMsgReturnStmt(a_Check, a_DbgPrintArgs, DbgPrint a_DbgPrintArgs, a_rcRet)
+#define MY_CHECK(a_Check, a_DbgPrintArgs) \
+ AssertMsgStmt(a_Check, a_DbgPrintArgs, DbgPrint a_DbgPrintArgs; rc = VERR_INTERNAL_ERROR_4 )
+
+ /*
+ * API combination checks.
+ */
+ MY_CHECK_RETURN(!g_pfnrtKeSetTargetProcessorDpcEx || g_pfnrtKeGetProcessorNumberFromIndex,
+ ("IPRT: Fatal: Missing KeSetTargetProcessorDpcEx without KeGetProcessorNumberFromIndex!\n"),
+ VERR_SYMBOL_NOT_FOUND);
+
+ /*
+ * Get max number of processor groups.
+ *
+ * We may need to upadjust this number below, because windows likes to keep
+ * all options open when it comes to hotplugged CPU group assignments. A
+ * server advertising up to 64 CPUs in the ACPI table will get a result of
+ * 64 from KeQueryMaximumGroupCount. That makes sense. However, when windows
+ * server 2012 does a two processor group setup for it, the sum of the
+ * GroupInfo[*].MaximumProcessorCount members below is 128. This is probably
+ * because windows doesn't want to make decisions grouping of hotpluggable CPUs.
+ * So, we need to bump the maximum count to 128 below do deal with this as we
+ * want to have valid CPU set indexes for all potential CPUs - how could we
+ * otherwise use the RTMpGetSet() result and also RTCpuSetCount(RTMpGetSet())
+ * should equal RTMpGetCount().
+ */
+ if (g_pfnrtKeQueryMaximumGroupCount)
+ {
+ g_cRtMpNtMaxGroups = g_pfnrtKeQueryMaximumGroupCount();
+ MY_CHECK_RETURN(g_cRtMpNtMaxGroups <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxGroups > 0,
+ ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u\n", g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS),
+ VERR_MP_TOO_MANY_CPUS);
+ }
+ else
+ g_cRtMpNtMaxGroups = 1;
+
+ /*
+ * Get max number CPUs.
+ * This also defines the range of NT CPU indexes, RTCPUID and index into RTCPUSET.
+ */
+ if (g_pfnrtKeQueryMaximumProcessorCountEx)
+ {
+ g_cRtMpNtMaxCpus = g_pfnrtKeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS);
+ MY_CHECK_RETURN(g_cRtMpNtMaxCpus <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxCpus > 0,
+ ("IPRT: Fatal: g_cRtMpNtMaxCpus=%u, max %u [KeQueryMaximumProcessorCountEx]\n",
+ g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS),
+ VERR_MP_TOO_MANY_CPUS);
+ }
+ else if (g_pfnrtKeQueryMaximumProcessorCount)
+ {
+ g_cRtMpNtMaxCpus = g_pfnrtKeQueryMaximumProcessorCount();
+ MY_CHECK_RETURN(g_cRtMpNtMaxCpus <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxCpus > 0,
+ ("IPRT: Fatal: g_cRtMpNtMaxCpus=%u, max %u [KeQueryMaximumProcessorCount]\n",
+ g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS),
+ VERR_MP_TOO_MANY_CPUS);
+ }
+ else if (g_pfnrtKeQueryActiveProcessors)
+ {
+ KAFFINITY fActiveProcessors = g_pfnrtKeQueryActiveProcessors();
+ MY_CHECK_RETURN(fActiveProcessors != 0,
+ ("IPRT: Fatal: KeQueryActiveProcessors returned 0!\n"),
+ VERR_INTERNAL_ERROR_2);
+ g_cRtMpNtMaxCpus = 0;
+ do
+ {
+ g_cRtMpNtMaxCpus++;
+ fActiveProcessors >>= 1;
+ } while (fActiveProcessors);
+ }
+ else
+ g_cRtMpNtMaxCpus = KeNumberProcessors;
+
+ /*
+ * Just because we're a bit paranoid about getting something wrong wrt to the
+ * kernel interfaces, we try 16 times to get the KeQueryActiveProcessorCountEx
+ * and KeQueryLogicalProcessorRelationship information to match up.
+ */
+ for (unsigned cTries = 0;; cTries++)
+ {
+ /*
+ * Get number of active CPUs.
+ */
+ if (g_pfnrtKeQueryActiveProcessorCountEx)
+ {
+ g_cRtMpNtActiveCpus = g_pfnrtKeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
+ MY_CHECK_RETURN(g_cRtMpNtActiveCpus <= g_cRtMpNtMaxCpus && g_cRtMpNtActiveCpus > 0,
+ ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u [KeQueryActiveProcessorCountEx]\n",
+ g_cRtMpNtMaxGroups, g_cRtMpNtMaxCpus),
+ VERR_MP_TOO_MANY_CPUS);
+ }
+ else if (g_pfnrtKeQueryActiveProcessorCount)
+ {
+ g_cRtMpNtActiveCpus = g_pfnrtKeQueryActiveProcessorCount(NULL);
+ MY_CHECK_RETURN(g_cRtMpNtActiveCpus <= g_cRtMpNtMaxCpus && g_cRtMpNtActiveCpus > 0,
+ ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u [KeQueryActiveProcessorCount]\n",
+ g_cRtMpNtMaxGroups, g_cRtMpNtMaxCpus),
+ VERR_MP_TOO_MANY_CPUS);
+ }
+ else
+ g_cRtMpNtActiveCpus = g_cRtMpNtMaxCpus;
+
+ /*
+ * Query the details for the groups to figure out which CPUs are online as
+ * well as the NT index limit.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aidRtMpNtByCpuSetIdx); i++)
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpNtByCpuSetIdx[i] = NIL_RTCPUID;
+#else
+ g_aidRtMpNtByCpuSetIdx[i] = i < g_cRtMpNtMaxCpus ? i : NIL_RTCPUID;
+#endif
+ for (unsigned idxGroup = 0; idxGroup < RT_ELEMENTS(g_aRtMpNtCpuGroups); idxGroup++)
+ {
+ g_aRtMpNtCpuGroups[idxGroup].cMaxCpus = 0;
+ g_aRtMpNtCpuGroups[idxGroup].cActiveCpus = 0;
+ for (unsigned idxMember = 0; idxMember < RT_ELEMENTS(g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers); idxMember++)
+ g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1;
+ }
+
+ if (g_pfnrtKeQueryLogicalProcessorRelationship)
+ {
+ MY_CHECK_RETURN(g_pfnrtKeGetProcessorIndexFromNumber,
+ ("IPRT: Fatal: Found KeQueryLogicalProcessorRelationship but not KeGetProcessorIndexFromNumber!\n"),
+ VERR_SYMBOL_NOT_FOUND);
+ MY_CHECK_RETURN(g_pfnrtKeGetProcessorNumberFromIndex,
+ ("IPRT: Fatal: Found KeQueryLogicalProcessorRelationship but not KeGetProcessorIndexFromNumber!\n"),
+ VERR_SYMBOL_NOT_FOUND);
+ MY_CHECK_RETURN(g_pfnrtKeSetTargetProcessorDpcEx,
+ ("IPRT: Fatal: Found KeQueryLogicalProcessorRelationship but not KeSetTargetProcessorDpcEx!\n"),
+ VERR_SYMBOL_NOT_FOUND);
+
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = NULL;
+ int rc = rtR0NtInitQueryGroupRelations(&pInfo);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ MY_CHECK(pInfo->Group.MaximumGroupCount == g_cRtMpNtMaxGroups,
+ ("IPRT: Fatal: MaximumGroupCount=%u != g_cRtMpNtMaxGroups=%u!\n",
+ pInfo->Group.MaximumGroupCount, g_cRtMpNtMaxGroups));
+ MY_CHECK(pInfo->Group.ActiveGroupCount > 0 && pInfo->Group.ActiveGroupCount <= g_cRtMpNtMaxGroups,
+ ("IPRT: Fatal: ActiveGroupCount=%u != g_cRtMpNtMaxGroups=%u!\n",
+ pInfo->Group.ActiveGroupCount, g_cRtMpNtMaxGroups));
+
+ /*
+ * First we need to recalc g_cRtMpNtMaxCpus (see above).
+ */
+ uint32_t cMaxCpus = 0;
+ uint32_t idxGroup;
+ for (idxGroup = 0; RT_SUCCESS(rc) && idxGroup < pInfo->Group.ActiveGroupCount; idxGroup++)
+ {
+ const PROCESSOR_GROUP_INFO *pGrpInfo = &pInfo->Group.GroupInfo[idxGroup];
+ MY_CHECK_BREAK(pGrpInfo->MaximumProcessorCount <= MAXIMUM_PROC_PER_GROUP,
+ ("IPRT: Fatal: MaximumProcessorCount=%u\n", pGrpInfo->MaximumProcessorCount));
+ MY_CHECK_BREAK(pGrpInfo->ActiveProcessorCount <= pGrpInfo->MaximumProcessorCount,
+ ("IPRT: Fatal: ActiveProcessorCount=%u > MaximumProcessorCount=%u\n",
+ pGrpInfo->ActiveProcessorCount, pGrpInfo->MaximumProcessorCount));
+ cMaxCpus += pGrpInfo->MaximumProcessorCount;
+ }
+ if (cMaxCpus > g_cRtMpNtMaxCpus && RT_SUCCESS(rc))
+ {
+ DbgPrint("IPRT: g_cRtMpNtMaxCpus=%u -> %u\n", g_cRtMpNtMaxCpus, cMaxCpus);
+#ifndef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ uint32_t i = RT_MIN(cMaxCpus, RT_ELEMENTS(g_aidRtMpNtByCpuSetIdx));
+ while (i-- > g_cRtMpNtMaxCpus)
+ g_aidRtMpNtByCpuSetIdx[i] = i;
+#endif
+ g_cRtMpNtMaxCpus = cMaxCpus;
+ if (g_cRtMpNtMaxGroups > RTCPUSET_MAX_CPUS)
+ {
+ MY_CHECK(g_cRtMpNtMaxGroups <= RTCPUSET_MAX_CPUS && g_cRtMpNtMaxGroups > 0,
+ ("IPRT: Fatal: g_cRtMpNtMaxGroups=%u, max %u\n", g_cRtMpNtMaxGroups, RTCPUSET_MAX_CPUS));
+ rc = VERR_MP_TOO_MANY_CPUS;
+ }
+ }
+
+ /*
+ * Calc online mask, partition IDs and such.
+ *
+ * Also check ASSUMPTIONS:
+ *
+ * 1. Processor indexes going from 0 and up to
+ * KeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS) - 1.
+ *
+ * 2. Currently valid processor indexes, i.e. accepted by
+ * KeGetProcessorIndexFromNumber & KeGetProcessorNumberFromIndex, goes
+ * from 0 thru KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS) - 1.
+ *
+ * 3. PROCESSOR_GROUP_INFO::MaximumProcessorCount gives the number of
+ * relevant bits in the ActiveProcessorMask (from LSB).
+ *
+ * 4. Active processor count found in KeQueryLogicalProcessorRelationship
+ * output matches what KeQueryActiveProcessorCountEx(ALL) returns.
+ *
+ * 5. Active + inactive processor counts in same does not exceed
+ * KeQueryMaximumProcessorCountEx(ALL).
+ *
+ * Note! Processor indexes are assigned as CPUs come online and are not
+ * preallocated according to group maximums. Since CPUS are only taken
+ * online and never offlined, this means that internal CPU bitmaps are
+ * never sparse and no time is wasted scanning unused bits.
+ *
+ * Unfortunately, it means that ring-3 cannot easily guess the index
+ * assignments when hotswapping is used, and must use GIP when available.
+ */
+ RTCpuSetEmpty(&g_rtMpNtCpuSet);
+ uint32_t cInactive = 0;
+ uint32_t cActive = 0;
+ uint32_t idxCpuMax = 0;
+ uint32_t idxCpuSetNextInactive = g_cRtMpNtMaxCpus - 1;
+ for (idxGroup = 0; RT_SUCCESS(rc) && idxGroup < pInfo->Group.ActiveGroupCount; idxGroup++)
+ {
+ const PROCESSOR_GROUP_INFO *pGrpInfo = &pInfo->Group.GroupInfo[idxGroup];
+ MY_CHECK_BREAK(pGrpInfo->MaximumProcessorCount <= MAXIMUM_PROC_PER_GROUP,
+ ("IPRT: Fatal: MaximumProcessorCount=%u\n", pGrpInfo->MaximumProcessorCount));
+ MY_CHECK_BREAK(pGrpInfo->ActiveProcessorCount <= pGrpInfo->MaximumProcessorCount,
+ ("IPRT: Fatal: ActiveProcessorCount=%u > MaximumProcessorCount=%u\n",
+ pGrpInfo->ActiveProcessorCount, pGrpInfo->MaximumProcessorCount));
+
+ g_aRtMpNtCpuGroups[idxGroup].cMaxCpus = pGrpInfo->MaximumProcessorCount;
+ g_aRtMpNtCpuGroups[idxGroup].cActiveCpus = pGrpInfo->ActiveProcessorCount;
+
+ for (uint32_t idxMember = 0; idxMember < pGrpInfo->MaximumProcessorCount; idxMember++)
+ {
+ PROCESSOR_NUMBER ProcNum;
+ ProcNum.Group = (USHORT)idxGroup;
+ ProcNum.Number = (UCHAR)idxMember;
+ ProcNum.Reserved = 0;
+ ULONG idxCpu = g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum);
+ if (idxCpu != INVALID_PROCESSOR_INDEX)
+ {
+ MY_CHECK_BREAK(idxCpu < g_cRtMpNtMaxCpus && idxCpu < RTCPUSET_MAX_CPUS, /* ASSUMPTION #1 */
+ ("IPRT: Fatal: idxCpu=%u >= g_cRtMpNtMaxCpus=%u (RTCPUSET_MAX_CPUS=%u)\n",
+ idxCpu, g_cRtMpNtMaxCpus, RTCPUSET_MAX_CPUS));
+ if (idxCpu > idxCpuMax)
+ idxCpuMax = idxCpu;
+ g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpu;
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpNtByCpuSetIdx[idxCpu] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember);
+#endif
+
+ ProcNum.Group = UINT16_MAX;
+ ProcNum.Number = UINT8_MAX;
+ ProcNum.Reserved = UINT8_MAX;
+ NTSTATUS rcNt = g_pfnrtKeGetProcessorNumberFromIndex(idxCpu, &ProcNum);
+ MY_CHECK_BREAK(NT_SUCCESS(rcNt),
+ ("IPRT: Fatal: KeGetProcessorNumberFromIndex(%u,) -> %#x!\n", idxCpu, rcNt));
+ MY_CHECK_BREAK(ProcNum.Group == idxGroup && ProcNum.Number == idxMember,
+ ("IPRT: Fatal: KeGetProcessorXxxxFromYyyy roundtrip error for %#x! Group: %u vs %u, Number: %u vs %u\n",
+ idxCpu, ProcNum.Group, idxGroup, ProcNum.Number, idxMember));
+
+ if (pGrpInfo->ActiveProcessorMask & RT_BIT_64(idxMember))
+ {
+ RTCpuSetAddByIndex(&g_rtMpNtCpuSet, idxCpu);
+ cActive++;
+ }
+ else
+ cInactive++; /* (This is a little unexpected, but not important as long as things add up below.) */
+ }
+ else
+ {
+ /* Must be not present / inactive when KeGetProcessorIndexFromNumber fails. */
+ MY_CHECK_BREAK(!(pGrpInfo->ActiveProcessorMask & RT_BIT_64(idxMember)),
+ ("IPRT: Fatal: KeGetProcessorIndexFromNumber(%u/%u) failed but CPU is active! cMax=%u cActive=%u fActive=%p\n",
+ idxGroup, idxMember, pGrpInfo->MaximumProcessorCount, pGrpInfo->ActiveProcessorCount,
+ pGrpInfo->ActiveProcessorMask));
+ cInactive++;
+ if (idxCpuSetNextInactive >= g_cRtMpNtActiveCpus)
+ {
+ g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpNtByCpuSetIdx[idxCpuSetNextInactive] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember)
+ | RTMPNT_ID_F_INACTIVE;
+#endif
+ idxCpuSetNextInactive--;
+ }
+ }
+ }
+ }
+
+ MY_CHECK(cInactive + cActive <= g_cRtMpNtMaxCpus, /* ASSUMPTION #5 (not '==' because of inactive groups) */
+ ("IPRT: Fatal: cInactive=%u + cActive=%u > g_cRtMpNtMaxCpus=%u\n", cInactive, cActive, g_cRtMpNtMaxCpus));
+
+ /* Deal with inactive groups using KeQueryMaximumProcessorCountEx or as
+ best as we can by as best we can by stipulating maximum member counts
+ from the previous group. */
+ if ( RT_SUCCESS(rc)
+ && idxGroup < pInfo->Group.MaximumGroupCount)
+ {
+ uint16_t cInactiveLeft = g_cRtMpNtMaxCpus - (cInactive + cActive);
+ while (idxGroup < pInfo->Group.MaximumGroupCount)
+ {
+ uint32_t cMaxMembers = 0;
+ if (g_pfnrtKeQueryMaximumProcessorCountEx)
+ cMaxMembers = g_pfnrtKeQueryMaximumProcessorCountEx(idxGroup);
+ if (cMaxMembers != 0 || cInactiveLeft == 0)
+ AssertStmt(cMaxMembers <= cInactiveLeft, cMaxMembers = cInactiveLeft);
+ else
+ {
+ uint16_t cGroupsLeft = pInfo->Group.MaximumGroupCount - idxGroup;
+ cMaxMembers = pInfo->Group.GroupInfo[idxGroup - 1].MaximumProcessorCount;
+ while (cMaxMembers * cGroupsLeft < cInactiveLeft)
+ cMaxMembers++;
+ if (cMaxMembers > cInactiveLeft)
+ cMaxMembers = cInactiveLeft;
+ }
+
+ g_aRtMpNtCpuGroups[idxGroup].cMaxCpus = (uint16_t)cMaxMembers;
+ g_aRtMpNtCpuGroups[idxGroup].cActiveCpus = 0;
+ for (uint16_t idxMember = 0; idxMember < cMaxMembers; idxMember++)
+ if (idxCpuSetNextInactive >= g_cRtMpNtActiveCpus)
+ {
+ g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpNtByCpuSetIdx[idxCpuSetNextInactive] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember)
+ | RTMPNT_ID_F_INACTIVE;
+#endif
+ idxCpuSetNextInactive--;
+ }
+ cInactiveLeft -= cMaxMembers;
+ idxGroup++;
+ }
+ }
+
+ /* We're done with pInfo now, free it so we can start returning when assertions fail. */
+ RTMemFree(pInfo);
+ if (RT_FAILURE(rc)) /* MY_CHECK_BREAK sets rc. */
+ return rc;
+ MY_CHECK_RETURN(cActive >= g_cRtMpNtActiveCpus,
+ ("IPRT: Fatal: cActive=%u < g_cRtMpNtActiveCpus=%u - CPUs removed?\n", cActive, g_cRtMpNtActiveCpus),
+ VERR_INTERNAL_ERROR_3);
+ MY_CHECK_RETURN(idxCpuMax < cActive, /* ASSUMPTION #2 */
+ ("IPRT: Fatal: idCpuMax=%u >= cActive=%u! Unexpected CPU index allocation. CPUs removed?\n",
+ idxCpuMax, cActive),
+ VERR_INTERNAL_ERROR_4);
+
+ /* Retry if CPUs were added. */
+ if ( cActive != g_cRtMpNtActiveCpus
+ && cTries < 16)
+ continue;
+ MY_CHECK_RETURN(cActive == g_cRtMpNtActiveCpus, /* ASSUMPTION #4 */
+ ("IPRT: Fatal: cActive=%u != g_cRtMpNtActiveCpus=%u\n", cActive, g_cRtMpNtActiveCpus),
+ VERR_INTERNAL_ERROR_5);
+ }
+ else
+ {
+ /* Legacy: */
+ MY_CHECK_RETURN(g_cRtMpNtMaxGroups == 1, ("IPRT: Fatal: Missing KeQueryLogicalProcessorRelationship!\n"),
+ VERR_SYMBOL_NOT_FOUND);
+
+ /** @todo Is it possible that the affinity mask returned by
+ * KeQueryActiveProcessors is sparse? */
+ if (g_pfnrtKeQueryActiveProcessors)
+ RTCpuSetFromU64(&g_rtMpNtCpuSet, g_pfnrtKeQueryActiveProcessors());
+ else if (g_cRtMpNtMaxCpus < 64)
+ RTCpuSetFromU64(&g_rtMpNtCpuSet, (UINT64_C(1) << g_cRtMpNtMaxCpus) - 1);
+ else
+ {
+ MY_CHECK_RETURN(g_cRtMpNtMaxCpus == 64, ("IPRT: Fatal: g_cRtMpNtMaxCpus=%u, expect 64 or less\n", g_cRtMpNtMaxCpus),
+ VERR_MP_TOO_MANY_CPUS);
+ RTCpuSetFromU64(&g_rtMpNtCpuSet, UINT64_MAX);
+ }
+
+ g_aRtMpNtCpuGroups[0].cMaxCpus = g_cRtMpNtMaxCpus;
+ g_aRtMpNtCpuGroups[0].cActiveCpus = g_cRtMpNtMaxCpus;
+ for (unsigned i = 0; i < g_cRtMpNtMaxCpus; i++)
+ {
+ g_aRtMpNtCpuGroups[0].aidxCpuSetMembers[i] = i;
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpNtByCpuSetIdx[i] = RTMPCPUID_FROM_GROUP_AND_NUMBER(0, i);
+#endif
+ }
+ }
+
+ /*
+ * Register CPU hot plugging callback (it also counts active CPUs).
+ */
+ Assert(g_pvMpCpuChangeCallback == NULL);
+ if (g_pfnrtKeRegisterProcessorChangeCallback)
+ {
+ MY_CHECK_RETURN(g_pfnrtKeDeregisterProcessorChangeCallback,
+ ("IPRT: Fatal: KeRegisterProcessorChangeCallback without KeDeregisterProcessorChangeCallback!\n"),
+ VERR_SYMBOL_NOT_FOUND);
+
+ RTCPUSET const ActiveSetCopy = g_rtMpNtCpuSet;
+ RTCpuSetEmpty(&g_rtMpNtCpuSet);
+ uint32_t const cActiveCpus = g_cRtMpNtActiveCpus;
+ g_cRtMpNtActiveCpus = 0;
+
+ g_pvMpCpuChangeCallback = g_pfnrtKeRegisterProcessorChangeCallback(rtR0NtMpProcessorChangeCallback, NULL /*pvUser*/,
+ KE_PROCESSOR_CHANGE_ADD_EXISTING);
+ if (g_pvMpCpuChangeCallback)
+ {
+ if (cActiveCpus == g_cRtMpNtActiveCpus)
+ { /* likely */ }
+ else
+ {
+ g_pfnrtKeDeregisterProcessorChangeCallback(g_pvMpCpuChangeCallback);
+ if (cTries < 16)
+ {
+ /* Retry if CPUs were added. */
+ MY_CHECK_RETURN(g_cRtMpNtActiveCpus >= cActiveCpus,
+ ("IPRT: Fatal: g_cRtMpNtActiveCpus=%u < cActiveCpus=%u! CPUs removed?\n",
+ g_cRtMpNtActiveCpus, cActiveCpus),
+ VERR_INTERNAL_ERROR_2);
+ MY_CHECK_RETURN(g_cRtMpNtActiveCpus <= g_cRtMpNtMaxCpus,
+ ("IPRT: Fatal: g_cRtMpNtActiveCpus=%u > g_cRtMpNtMaxCpus=%u!\n",
+ g_cRtMpNtActiveCpus, g_cRtMpNtMaxCpus),
+ VERR_INTERNAL_ERROR_2);
+ continue;
+ }
+ MY_CHECK_RETURN(0, ("IPRT: Fatal: g_cRtMpNtActiveCpus=%u cActiveCpus=%u\n", g_cRtMpNtActiveCpus, cActiveCpus),
+ VERR_INTERNAL_ERROR_3);
+ }
+ }
+ else
+ {
+ AssertFailed();
+ g_rtMpNtCpuSet = ActiveSetCopy;
+ g_cRtMpNtActiveCpus = cActiveCpus;
+ }
+ }
+ break;
+ } /* Retry loop for stable active CPU count. */
+
+#undef MY_CHECK_RETURN
+
+ /*
+ * Special IPI fun for RTMpPokeCpu.
+ *
+ * On Vista and later the DPC method doesn't seem to reliably send IPIs,
+ * so we have to use alternative methods.
+ *
+ * On AMD64 We used to use the HalSendSoftwareInterrupt API (also x86 on
+ * W10+), it looks faster and more convenient to use, however we're either
+ * using it wrong or it doesn't reliably do what we want (see @bugref{8343}).
+ *
+ * The HalRequestIpip API is thus far the only alternative to KeInsertQueueDpc
+ * for doing targetted IPIs. Trouble with this API is that it changed
+ * fundamentally in Window 7 when they added support for lots of processors.
+ *
+ * If we really think we cannot use KeInsertQueueDpc, we use the broadcast IPI
+ * API KeIpiGenericCall.
+ */
+ if ( pOsVerInfo->uMajorVer > 6
+ || (pOsVerInfo->uMajorVer == 6 && pOsVerInfo->uMinorVer > 0))
+ g_pfnrtHalRequestIpiPreW7 = NULL;
+ else
+ g_pfnrtHalRequestIpiW7Plus = NULL;
+
+ if ( g_pfnrtHalRequestIpiW7Plus
+ && g_pfnrtKeInitializeAffinityEx
+ && g_pfnrtKeAddProcessorAffinityEx
+ && g_pfnrtKeGetProcessorIndexFromNumber)
+ {
+ /* Determine the real size of the KAFFINITY_EX structure. */
+ size_t const cbAffinity = _8K;
+ PKAFFINITY_EX pAffinity = (PKAFFINITY_EX)RTMemAllocZ(cbAffinity);
+ AssertReturn(pAffinity, VERR_NO_MEMORY);
+ size_t const cMaxEntries = (cbAffinity - RT_UOFFSETOF(KAFFINITY_EX, Bitmap[0])) / sizeof(pAffinity->Bitmap[0]);
+ g_pfnrtKeInitializeAffinityEx(pAffinity);
+ if (pAffinity->Size > 1 && pAffinity->Size <= cMaxEntries)
+ {
+ g_cRtMpNtKaffinityExEntries = pAffinity->Size;
+ g_cbRtMpNtKaffinityEx = pAffinity->Size * sizeof(pAffinity->Bitmap[0]) + RT_UOFFSETOF(KAFFINITY_EX, Bitmap[0]);
+ g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingHalRequestIpiW7Plus;
+ RTMemFree(pAffinity);
+ DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingHalRequestIpiW7Plus\n");
+ return VINF_SUCCESS;
+ }
+ DbgPrint("IPRT: RTMpPoke can't use rtMpPokeCpuUsingHalRequestIpiW7Plus! pAffinity->Size=%u\n", pAffinity->Size);
+ AssertReleaseMsg(pAffinity->Size <= cMaxEntries, ("%#x\n", pAffinity->Size)); /* stack is toast if larger (32768 CPUs). */
+ RTMemFree(pAffinity);
+ }
+
+ if (pOsVerInfo->uMajorVer >= 6 && g_pfnrtKeIpiGenericCall)
+ {
+ DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingBroadcastIpi\n");
+ g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingBroadcastIpi;
+ }
+ else if (g_pfnrtKeSetTargetProcessorDpc)
+ {
+ DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingDpc\n");
+ g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingDpc;
+ /* Windows XP should send always send an IPI -> VERIFY */
+ }
+ else
+ {
+ DbgPrint("IPRT: RTMpPoke => rtMpPokeCpuUsingFailureNotSupported\n");
+ Assert(pOsVerInfo->uMajorVer == 3 && pOsVerInfo->uMinorVer <= 50);
+ g_pfnrtMpPokeCpuWorker = rtMpPokeCpuUsingFailureNotSupported;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Called by rtR0TermNative.
+ */
+DECLHIDDEN(void) rtR0MpNtTerm(void)
+{
+ /*
+ * Deregister the processor change callback.
+ */
+ PVOID pvMpCpuChangeCallback = g_pvMpCpuChangeCallback;
+ g_pvMpCpuChangeCallback = NULL;
+ if (pvMpCpuChangeCallback)
+ {
+ AssertReturnVoid(g_pfnrtKeDeregisterProcessorChangeCallback);
+ g_pfnrtKeDeregisterProcessorChangeCallback(pvMpCpuChangeCallback);
+ }
+}
+
+
+DECLHIDDEN(int) rtR0MpNotificationNativeInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void)
+{
+}
+
+
+/**
+ * Implements the NT PROCESSOR_CALLBACK_FUNCTION callback function.
+ *
+ * This maintains the g_rtMpNtCpuSet and works MP notification callbacks. When
+ * registered, it's called for each active CPU in the system, avoiding racing
+ * CPU hotplugging (as well as testing the callback).
+ *
+ * @param pvUser User context (not used).
+ * @param pChangeCtx Change context (in).
+ * @param prcOperationStatus Operation status (in/out).
+ *
+ * @remarks ASSUMES no concurrent execution of KeProcessorAddCompleteNotify
+ * notification callbacks. At least during callback registration
+ * callout, we're owning KiDynamicProcessorLock.
+ *
+ * @remarks When registering the handler, we first get KeProcessorAddStartNotify
+ * callbacks for all active CPUs, and after they all succeed we get the
+ * KeProcessorAddCompleteNotify callbacks.
+ */
+static VOID __stdcall rtR0NtMpProcessorChangeCallback(void *pvUser, PKE_PROCESSOR_CHANGE_NOTIFY_CONTEXT pChangeCtx,
+ PNTSTATUS prcOperationStatus)
+{
+ RT_NOREF(pvUser, prcOperationStatus);
+ switch (pChangeCtx->State)
+ {
+ /*
+ * Check whether we can deal with the CPU, failing the start operation if we
+ * can't. The checks we are doing here are to avoid complicated/impossible
+ * cases in KeProcessorAddCompleteNotify. They are really just verify specs.
+ */
+ case KeProcessorAddStartNotify:
+ {
+ NTSTATUS rcNt = STATUS_SUCCESS;
+ if (pChangeCtx->NtNumber < RTCPUSET_MAX_CPUS)
+ {
+ if (pChangeCtx->NtNumber >= g_cRtMpNtMaxCpus)
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: NtNumber=%u is higher than the max CPU count (%u)!\n",
+ pChangeCtx->NtNumber, g_cRtMpNtMaxCpus);
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+
+ /* The ProcessNumber field was introduced in Windows 7. */
+ PROCESSOR_NUMBER ProcNum;
+ if (g_pfnrtKeGetProcessorIndexFromNumber)
+ {
+ ProcNum = pChangeCtx->ProcNumber;
+ KEPROCESSORINDEX idxCpu = g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum);
+ if (idxCpu != pChangeCtx->NtNumber)
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: g_pfnrtKeGetProcessorIndexFromNumber(%u.%u) -> %u, expected %u!\n",
+ ProcNum.Group, ProcNum.Number, idxCpu, pChangeCtx->NtNumber);
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ ProcNum.Group = 0;
+ ProcNum.Number = pChangeCtx->NtNumber;
+ }
+
+ if ( ProcNum.Group < RT_ELEMENTS(g_aRtMpNtCpuGroups)
+ && ProcNum.Number < RT_ELEMENTS(g_aRtMpNtCpuGroups[0].aidxCpuSetMembers))
+ {
+ if (ProcNum.Group >= g_cRtMpNtMaxGroups)
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: %u.%u is out of range - max groups: %u!\n",
+ ProcNum.Group, ProcNum.Number, g_cRtMpNtMaxGroups);
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+
+ if (ProcNum.Number < g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus)
+ {
+ Assert(g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] != -1);
+ if (g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] == -1)
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: Internal error! %u.%u was assigned -1 as set index!\n",
+ ProcNum.Group, ProcNum.Number);
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+
+ Assert(g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] != NIL_RTCPUID);
+ if (g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] == NIL_RTCPUID)
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: Internal error! %u (%u.%u) translates to NIL_RTCPUID!\n",
+ pChangeCtx->NtNumber, ProcNum.Group, ProcNum.Number);
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: max processors in group %u is %u, cannot add %u to it!\n",
+ ProcNum.Group, g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus, ProcNum.Group, ProcNum.Number);
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: %u.%u is out of range (max %u.%u)!\n",
+ ProcNum.Group, ProcNum.Number, RT_ELEMENTS(g_aRtMpNtCpuGroups), RT_ELEMENTS(g_aRtMpNtCpuGroups[0].aidxCpuSetMembers));
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+ }
+ else
+ {
+ DbgPrint("IPRT: KeProcessorAddStartNotify failure: NtNumber=%u is outside RTCPUSET_MAX_CPUS (%u)!\n",
+ pChangeCtx->NtNumber, RTCPUSET_MAX_CPUS);
+ rcNt = STATUS_INTERNAL_ERROR;
+ }
+ if (!NT_SUCCESS(rcNt))
+ *prcOperationStatus = rcNt;
+ break;
+ }
+
+ /*
+ * Update the globals. Since we've checked out range limits and other
+ * limitations already we just AssertBreak here.
+ */
+ case KeProcessorAddCompleteNotify:
+ {
+ /*
+ * Calc the processor number and assert conditions checked in KeProcessorAddStartNotify.
+ */
+ AssertBreak(pChangeCtx->NtNumber < RTCPUSET_MAX_CPUS);
+ AssertBreak(pChangeCtx->NtNumber < g_cRtMpNtMaxCpus);
+ Assert(pChangeCtx->NtNumber == g_cRtMpNtActiveCpus); /* light assumption */
+ PROCESSOR_NUMBER ProcNum;
+ if (g_pfnrtKeGetProcessorIndexFromNumber)
+ {
+ ProcNum = pChangeCtx->ProcNumber;
+ AssertBreak(g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum) == pChangeCtx->NtNumber);
+ AssertBreak(ProcNum.Group < RT_ELEMENTS(g_aRtMpNtCpuGroups));
+ AssertBreak(ProcNum.Group < g_cRtMpNtMaxGroups);
+ }
+ else
+ {
+ ProcNum.Group = 0;
+ ProcNum.Number = pChangeCtx->NtNumber;
+ }
+ AssertBreak(ProcNum.Number < RT_ELEMENTS(g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers));
+ AssertBreak(ProcNum.Number < g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus);
+ AssertBreak(g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] != -1);
+ AssertBreak(g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] != NIL_RTCPUID);
+
+ /*
+ * Add ourselves to the online CPU set and update the active CPU count.
+ */
+ RTCpuSetAddByIndex(&g_rtMpNtCpuSet, pChangeCtx->NtNumber);
+ ASMAtomicIncU32(&g_cRtMpNtActiveCpus);
+
+ /*
+ * Update the group info.
+ *
+ * If the index prediction failed (real hotplugging callbacks only) we
+ * have to switch it around. This is particularly annoying when we
+ * use the index as the ID.
+ */
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ RTCPUID idCpu = RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number);
+ RTCPUID idOld = g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber];
+ if ((idOld & ~RTMPNT_ID_F_INACTIVE) != idCpu)
+ {
+ Assert(idOld & RTMPNT_ID_F_INACTIVE);
+ int idxDest = g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number];
+ g_aRtMpNtCpuGroups[rtMpCpuIdGetGroup(idOld)].aidxCpuSetMembers[rtMpCpuIdGetGroupMember(idOld)] = idxDest;
+ g_aidRtMpNtByCpuSetIdx[idxDest] = idOld;
+ }
+ g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] = idCpu;
+#else
+ Assert(g_aidRtMpNtByCpuSetIdx[pChangeCtx->NtNumber] == pChangeCtx->NtNumber);
+ int idxDest = g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number];
+ if ((ULONG)idxDest != pChangeCtx->NtNumber)
+ {
+ bool fFound = false;
+ uint32_t idxOldGroup = g_cRtMpNtMaxGroups;
+ while (idxOldGroup-- > 0 && !fFound)
+ {
+ uint32_t idxMember = g_aRtMpNtCpuGroups[idxOldGroup].cMaxCpus;
+ while (idxMember-- > 0)
+ if (g_aRtMpNtCpuGroups[idxOldGroup].aidxCpuSetMembers[idxMember] == (int)pChangeCtx->NtNumber)
+ {
+ g_aRtMpNtCpuGroups[idxOldGroup].aidxCpuSetMembers[idxMember] = idxDest;
+ fFound = true;
+ break;
+ }
+ }
+ Assert(fFound);
+ }
+#endif
+ g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] = pChangeCtx->NtNumber;
+
+ /*
+ * Do MP notification callbacks.
+ */
+ rtMpNotificationDoCallbacks(RTMPEVENT_ONLINE, pChangeCtx->NtNumber);
+ break;
+ }
+
+ case KeProcessorAddFailureNotify:
+ /* ignore */
+ break;
+
+ default:
+ AssertMsgFailed(("State=%u\n", pChangeCtx->State));
+ }
+}
+
+
+/**
+ * Wrapper around KeQueryLogicalProcessorRelationship.
+ *
+ * @returns IPRT status code.
+ * @param ppInfo Where to return the info. Pass to RTMemFree when done.
+ */
+static int rtR0NtInitQueryGroupRelations(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX **ppInfo)
+{
+ ULONG cbInfo = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + g_cRtMpNtMaxGroups * sizeof(GROUP_RELATIONSHIP);
+ NTSTATUS rcNt;
+ do
+ {
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)RTMemAlloc(cbInfo);
+ if (pInfo)
+ {
+ rcNt = g_pfnrtKeQueryLogicalProcessorRelationship(NULL /*pProcNumber*/, RelationGroup, pInfo, &cbInfo);
+ if (NT_SUCCESS(rcNt))
+ {
+ *ppInfo = pInfo;
+ return VINF_SUCCESS;
+ }
+
+ RTMemFree(pInfo);
+ pInfo = NULL;
+ }
+ else
+ rcNt = STATUS_NO_MEMORY;
+ } while (rcNt == STATUS_INFO_LENGTH_MISMATCH);
+ DbgPrint("IPRT: Fatal: KeQueryLogicalProcessorRelationship failed: %#x\n", rcNt);
+ AssertMsgFailed(("KeQueryLogicalProcessorRelationship failed: %#x\n", rcNt));
+ return RTErrConvertFromNtStatus(rcNt);
+}
+
+
+
+
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ PROCESSOR_NUMBER ProcNum;
+ ProcNum.Group = 0;
+ if (g_pfnrtKeGetCurrentProcessorNumberEx)
+ {
+ ProcNum.Number = 0;
+ g_pfnrtKeGetCurrentProcessorNumberEx(&ProcNum);
+ }
+ else
+ ProcNum.Number = KeGetCurrentProcessorNumber(); /* Number is 8-bit, so we're not subject to BYTE -> WORD upgrade in WDK. */
+ return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number);
+
+#else
+
+ if (g_pfnrtKeGetCurrentProcessorNumberEx)
+ {
+ KEPROCESSORINDEX idxCpu = g_pfnrtKeGetCurrentProcessorNumberEx(NULL);
+ Assert(idxCpu < RTCPUSET_MAX_CPUS);
+ return idxCpu;
+ }
+
+ return (uint8_t)KeGetCurrentProcessorNumber(); /* PCR->Number was changed from BYTE to WORD in the WDK, thus the cast. */
+#endif
+}
+
+
+RTDECL(int) RTMpCurSetIndex(void)
+{
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ if (g_pfnrtKeGetCurrentProcessorNumberEx)
+ {
+ KEPROCESSORINDEX idxCpu = g_pfnrtKeGetCurrentProcessorNumberEx(NULL);
+ Assert(idxCpu < RTCPUSET_MAX_CPUS);
+ return idxCpu;
+ }
+ return (uint8_t)KeGetCurrentProcessorNumber(); /* PCR->Number was changed from BYTE to WORD in the WDK, thus the cast. */
+#else
+ return (int)RTMpCpuId();
+#endif
+}
+
+
+RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu)
+{
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ PROCESSOR_NUMBER ProcNum = { 0 , 0, 0 };
+ KEPROCESSORINDEX idxCpu = g_pfnrtKeGetCurrentProcessorNumberEx(&ProcNum);
+ Assert(idxCpu < RTCPUSET_MAX_CPUS);
+ *pidCpu = RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number);
+ return idxCpu;
+#else
+ return *pidCpu = RTMpCpuId();
+#endif
+}
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ if (idCpu != NIL_RTCPUID)
+ {
+ if (g_pfnrtKeGetProcessorIndexFromNumber)
+ {
+ PROCESSOR_NUMBER ProcNum;
+ ProcNum.Group = rtMpCpuIdGetGroup(idCpu);
+ ProcNum.Number = rtMpCpuIdGetGroupMember(idCpu);
+ ProcNum.Reserved = 0;
+ KEPROCESSORINDEX idxCpu = g_pfnrtKeGetProcessorIndexFromNumber(&ProcNum);
+ if (idxCpu != INVALID_PROCESSOR_INDEX)
+ {
+ Assert(idxCpu < g_cRtMpNtMaxCpus);
+ Assert((ULONG)g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number] == idxCpu);
+ return idxCpu;
+ }
+
+ /* Since NT assigned indexes as the CPUs come online, we cannot produce an ID <-> index
+ mapping for not-yet-onlined CPUS that is consistent. We just have to do our best... */
+ if ( ProcNum.Group < g_cRtMpNtMaxGroups
+ && ProcNum.Number < g_aRtMpNtCpuGroups[ProcNum.Group].cMaxCpus)
+ return g_aRtMpNtCpuGroups[ProcNum.Group].aidxCpuSetMembers[ProcNum.Number];
+ }
+ else if (rtMpCpuIdGetGroup(idCpu) == 0)
+ return rtMpCpuIdGetGroupMember(idCpu);
+ }
+ return -1;
+#else
+ /* 1:1 mapping, just do range checks. */
+ return idCpu < RTCPUSET_MAX_CPUS ? (int)idCpu : -1;
+#endif
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ if ((unsigned)iCpu < g_cRtMpNtMaxCpus)
+ {
+ if (g_pfnrtKeGetProcessorIndexFromNumber)
+ {
+ PROCESSOR_NUMBER ProcNum = { 0, 0, 0 };
+ NTSTATUS rcNt = g_pfnrtKeGetProcessorNumberFromIndex(iCpu, &ProcNum);
+ if (NT_SUCCESS(rcNt))
+ {
+ Assert(ProcNum.Group <= g_cRtMpNtMaxGroups);
+ Assert( (g_aidRtMpNtByCpuSetIdx[iCpu] & ~RTMPNT_ID_F_INACTIVE)
+ == RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number));
+ return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number);
+ }
+ }
+ return g_aidRtMpNtByCpuSetIdx[iCpu];
+ }
+ return NIL_RTCPUID;
+#else
+ /* 1:1 mapping, just do range checks. */
+ return (unsigned)iCpu < RTCPUSET_MAX_CPUS ? iCpu : NIL_RTCPUID;
+#endif
+}
+
+
+RTDECL(int) RTMpSetIndexFromCpuGroupMember(uint32_t idxGroup, uint32_t idxMember)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ if (idxGroup < g_cRtMpNtMaxGroups)
+ if (idxMember < g_aRtMpNtCpuGroups[idxGroup].cMaxCpus)
+ return g_aRtMpNtCpuGroups[idxGroup].aidxCpuSetMembers[idxMember];
+ return -1;
+}
+
+
+RTDECL(uint32_t) RTMpGetCpuGroupCounts(uint32_t idxGroup, uint32_t *pcActive)
+{
+ if (idxGroup < g_cRtMpNtMaxGroups)
+ {
+ if (pcActive)
+ *pcActive = g_aRtMpNtCpuGroups[idxGroup].cActiveCpus;
+ return g_aRtMpNtCpuGroups[idxGroup].cMaxCpus;
+ }
+ if (pcActive)
+ *pcActive = 0;
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxCpuGroupCount(void)
+{
+ return g_cRtMpNtMaxGroups;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ return RTMPCPUID_FROM_GROUP_AND_NUMBER(g_cRtMpNtMaxGroups - 1, g_aRtMpNtCpuGroups[g_cRtMpNtMaxGroups - 1].cMaxCpus - 1);
+#else
+ /* According to MSDN the processor indexes goes from 0 to the maximum
+ number of CPUs in the system. We've check this in initterm-r0drv-nt.cpp. */
+ return g_cRtMpNtMaxCpus - 1;
+#endif
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+ return RTCpuSetIsMember(&g_rtMpNtCpuSet, idCpu);
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ if (idCpu != NIL_RTCPUID)
+ {
+ unsigned idxGroup = rtMpCpuIdGetGroup(idCpu);
+ if (idxGroup < g_cRtMpNtMaxGroups)
+ return rtMpCpuIdGetGroupMember(idCpu) < g_aRtMpNtCpuGroups[idxGroup].cMaxCpus;
+ }
+ return false;
+
+#else
+ /* A possible CPU ID is one with a value lower than g_cRtMpNtMaxCpus (see
+ comment in RTMpGetMaxCpuId). */
+ return idCpu < g_cRtMpNtMaxCpus;
+#endif
+}
+
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ /* The set of possible CPU IDs(/indexes) are from 0 up to
+ g_cRtMpNtMaxCpus (see comment in RTMpGetMaxCpuId). */
+ RTCpuSetEmpty(pSet);
+ int idxCpu = g_cRtMpNtMaxCpus;
+ while (idxCpu-- > 0)
+ RTCpuSetAddByIndex(pSet, idxCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+ return g_cRtMpNtMaxCpus;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ *pSet = g_rtMpNtCpuSet;
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
+{
+ /** @todo fix me */
+ return RTMpGetOnlineCount();
+}
+
+
+
+#if 0
+/* Experiment with checking the undocumented KPRCB structure
+ * 'dt nt!_kprcb 0xaddress' shows the layout
+ */
+typedef struct
+{
+ LIST_ENTRY DpcListHead;
+ ULONG_PTR DpcLock;
+ volatile ULONG DpcQueueDepth;
+ ULONG DpcQueueCount;
+} KDPC_DATA, *PKDPC_DATA;
+
+RTDECL(bool) RTMpIsCpuWorkPending(void)
+{
+ uint8_t *pkprcb;
+ PKDPC_DATA pDpcData;
+
+ _asm {
+ mov eax, fs:0x20
+ mov pkprcb, eax
+ }
+ pDpcData = (PKDPC_DATA)(pkprcb + 0x19e0);
+ if (pDpcData->DpcQueueDepth)
+ return true;
+
+ pDpcData++;
+ if (pDpcData->DpcQueueDepth)
+ return true;
+ return false;
+}
+#else
+RTDECL(bool) RTMpIsCpuWorkPending(void)
+{
+ /** @todo not implemented */
+ return false;
+}
+#endif
+
+
+/**
+ * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for
+ * the RTMpOnAll case.
+ *
+ * @param uUserCtx The user context argument (PRTMPARGS).
+ */
+static ULONG_PTR rtmpNtOnAllBroadcastIpiWrapper(ULONG_PTR uUserCtx)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)uUserCtx;
+ /*ASMAtomicIncU32(&pArgs->cHits); - not needed */
+ pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2);
+ return 0;
+}
+
+
+/**
+ * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for
+ * the RTMpOnOthers case.
+ *
+ * @param uUserCtx The user context argument (PRTMPARGS).
+ */
+static ULONG_PTR rtmpNtOnOthersBroadcastIpiWrapper(ULONG_PTR uUserCtx)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)uUserCtx;
+ RTCPUID idCpu = RTMpCpuId();
+ if (pArgs->idCpu != idCpu)
+ {
+ /*ASMAtomicIncU32(&pArgs->cHits); - not needed */
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ }
+ return 0;
+}
+
+
+/**
+ * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for
+ * the RTMpOnPair case.
+ *
+ * @param uUserCtx The user context argument (PRTMPARGS).
+ */
+static ULONG_PTR rtmpNtOnPairBroadcastIpiWrapper(ULONG_PTR uUserCtx)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)uUserCtx;
+ RTCPUID idCpu = RTMpCpuId();
+ if ( pArgs->idCpu == idCpu
+ || pArgs->idCpu2 == idCpu)
+ {
+ ASMAtomicIncU32(&pArgs->cHits);
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ }
+ return 0;
+}
+
+
+/**
+ * Wrapper between the native KIPI_BROADCAST_WORKER and IPRT's PFNRTMPWORKER for
+ * the RTMpOnSpecific case.
+ *
+ * @param uUserCtx The user context argument (PRTMPARGS).
+ */
+static ULONG_PTR rtmpNtOnSpecificBroadcastIpiWrapper(ULONG_PTR uUserCtx)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)uUserCtx;
+ RTCPUID idCpu = RTMpCpuId();
+ if (pArgs->idCpu == idCpu)
+ {
+ ASMAtomicIncU32(&pArgs->cHits);
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ }
+ return 0;
+}
+
+
+/**
+ * Internal worker for the RTMpOn* APIs using KeIpiGenericCall.
+ *
+ * @returns VINF_SUCCESS.
+ * @param pfnWorker The callback.
+ * @param pvUser1 User argument 1.
+ * @param pvUser2 User argument 2.
+ * @param pfnNativeWrapper The wrapper between the NT and IPRT callbacks.
+ * @param idCpu First CPU to match, ultimately specific to the
+ * pfnNativeWrapper used.
+ * @param idCpu2 Second CPU to match, ultimately specific to the
+ * pfnNativeWrapper used.
+ * @param pcHits Where to return the number of this. Optional.
+ */
+static int rtMpCallUsingBroadcastIpi(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2,
+ PKIPI_BROADCAST_WORKER pfnNativeWrapper, RTCPUID idCpu, RTCPUID idCpu2,
+ uint32_t *pcHits)
+{
+ RTMPARGS Args;
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu;
+ Args.idCpu2 = idCpu2;
+ Args.cRefs = 0;
+ Args.cHits = 0;
+
+ AssertPtr(g_pfnrtKeIpiGenericCall);
+ g_pfnrtKeIpiGenericCall(pfnNativeWrapper, (uintptr_t)&Args);
+ if (pcHits)
+ *pcHits = Args.cHits;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native nt per-cpu callbacks and PFNRTWORKER
+ *
+ * @param Dpc DPC object
+ * @param DeferredContext Context argument specified by KeInitializeDpc
+ * @param SystemArgument1 Argument specified by KeInsertQueueDpc
+ * @param SystemArgument2 Argument specified by KeInsertQueueDpc
+ */
+static VOID rtmpNtDPCWrapper(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)DeferredContext;
+ RT_NOREF3(Dpc, SystemArgument1, SystemArgument2);
+
+ ASMAtomicIncU32(&pArgs->cHits);
+ pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2);
+
+ /* Dereference the argument structure. */
+ int32_t cRefs = ASMAtomicDecS32(&pArgs->cRefs);
+ Assert(cRefs >= 0);
+ if (cRefs == 0)
+ RTMemFree(pArgs);
+}
+
+
+/**
+ * Wrapper around KeSetTargetProcessorDpcEx / KeSetTargetProcessorDpc.
+ *
+ * This is shared with the timer code.
+ *
+ * @returns IPRT status code (errors are asserted).
+ * @retval VERR_CPU_NOT_FOUND if impossible CPU. Not asserted.
+ * @param pDpc The DPC.
+ * @param idCpu The ID of the new target CPU.
+ * @note Callable at any IRQL.
+ */
+DECLHIDDEN(int) rtMpNtSetTargetProcessorDpc(KDPC *pDpc, RTCPUID idCpu)
+{
+ if (g_pfnrtKeSetTargetProcessorDpcEx)
+ {
+ /* Convert to stupid process number (bet KeSetTargetProcessorDpcEx does
+ the reverse conversion internally). */
+ PROCESSOR_NUMBER ProcNum;
+ NTSTATUS rcNt = g_pfnrtKeGetProcessorNumberFromIndex(RTMpCpuIdToSetIndex(idCpu), &ProcNum);
+ if (NT_SUCCESS(rcNt))
+ {
+ rcNt = g_pfnrtKeSetTargetProcessorDpcEx(pDpc, &ProcNum);
+ AssertLogRelMsgReturn(NT_SUCCESS(rcNt),
+ ("KeSetTargetProcessorDpcEx(,%u(%u/%u)) -> %#x\n", idCpu, ProcNum.Group, ProcNum.Number, rcNt),
+ RTErrConvertFromNtStatus(rcNt));
+ }
+ else if (rcNt == STATUS_INVALID_PARAMETER)
+ return VERR_CPU_NOT_FOUND;
+ else
+ AssertLogRelMsgReturn(NT_SUCCESS(rcNt), ("KeGetProcessorNumberFromIndex(%u) -> %#x\n", idCpu, rcNt),
+ RTErrConvertFromNtStatus(rcNt));
+
+ }
+ else if (g_pfnrtKeSetTargetProcessorDpc)
+ g_pfnrtKeSetTargetProcessorDpc(pDpc, RTMpCpuIdToSetIndex(idCpu));
+ else
+ return VERR_NOT_SUPPORTED;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal worker for the RTMpOn* APIs.
+ *
+ * @returns IPRT status code.
+ * @param pfnWorker The callback.
+ * @param pvUser1 User argument 1.
+ * @param pvUser2 User argument 2.
+ * @param enmCpuid What to do / is idCpu valid.
+ * @param idCpu Used if enmCpuid is RT_NT_CPUID_SPECIFIC or
+ * RT_NT_CPUID_PAIR, otherwise ignored.
+ * @param idCpu2 Used if enmCpuid is RT_NT_CPUID_PAIR, otherwise ignored.
+ * @param pcHits Where to return the number of this. Optional.
+ */
+static int rtMpCallUsingDpcs(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2,
+ RT_NT_CPUID enmCpuid, RTCPUID idCpu, RTCPUID idCpu2, uint32_t *pcHits)
+{
+#if 0
+ /* KeFlushQueuedDpcs must be run at IRQL PASSIVE_LEVEL according to MSDN, but the
+ * driver verifier doesn't complain...
+ */
+ AssertMsg(KeGetCurrentIrql() == PASSIVE_LEVEL, ("%d != %d (PASSIVE_LEVEL)\n", KeGetCurrentIrql(), PASSIVE_LEVEL));
+#endif
+ /* KeFlushQueuedDpcs is not present in Windows 2000; import it dynamically so we can just fail this call. */
+ if (!g_pfnrtNtKeFlushQueuedDpcs)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Make a copy of the active CPU set and figure out how many KDPCs we really need.
+ * We must not try setup DPCs for CPUs which aren't there, because that may fail.
+ */
+ RTCPUSET OnlineSet = g_rtMpNtCpuSet;
+ uint32_t cDpcsNeeded;
+ switch (enmCpuid)
+ {
+ case RT_NT_CPUID_SPECIFIC:
+ cDpcsNeeded = 1;
+ break;
+ case RT_NT_CPUID_PAIR:
+ cDpcsNeeded = 2;
+ break;
+ default:
+ do
+ {
+ cDpcsNeeded = g_cRtMpNtActiveCpus;
+ OnlineSet = g_rtMpNtCpuSet;
+ } while (cDpcsNeeded != g_cRtMpNtActiveCpus);
+ break;
+ }
+
+ /*
+ * Allocate an RTMPARGS structure followed by cDpcsNeeded KDPCs
+ * and initialize them.
+ */
+ PRTMPARGS pArgs = (PRTMPARGS)RTMemAllocZ(sizeof(RTMPARGS) + cDpcsNeeded * sizeof(KDPC));
+ if (!pArgs)
+ return VERR_NO_MEMORY;
+
+ pArgs->pfnWorker = pfnWorker;
+ pArgs->pvUser1 = pvUser1;
+ pArgs->pvUser2 = pvUser2;
+ pArgs->idCpu = NIL_RTCPUID;
+ pArgs->idCpu2 = NIL_RTCPUID;
+ pArgs->cHits = 0;
+ pArgs->cRefs = 1;
+
+ int rc;
+ KDPC *paExecCpuDpcs = (KDPC *)(pArgs + 1);
+ if (enmCpuid == RT_NT_CPUID_SPECIFIC)
+ {
+ KeInitializeDpc(&paExecCpuDpcs[0], rtmpNtDPCWrapper, pArgs);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[0], HighImportance);
+ rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[0], idCpu);
+ pArgs->idCpu = idCpu;
+ }
+ else if (enmCpuid == RT_NT_CPUID_PAIR)
+ {
+ KeInitializeDpc(&paExecCpuDpcs[0], rtmpNtDPCWrapper, pArgs);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[0], HighImportance);
+ rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[0], idCpu);
+ pArgs->idCpu = idCpu;
+
+ KeInitializeDpc(&paExecCpuDpcs[1], rtmpNtDPCWrapper, pArgs);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[1], HighImportance);
+ if (RT_SUCCESS(rc))
+ rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[1], (int)idCpu2);
+ pArgs->idCpu2 = idCpu2;
+ }
+ else
+ {
+ rc = VINF_SUCCESS;
+ for (uint32_t i = 0; i < cDpcsNeeded && RT_SUCCESS(rc); i++)
+ if (RTCpuSetIsMemberByIndex(&OnlineSet, i))
+ {
+ KeInitializeDpc(&paExecCpuDpcs[i], rtmpNtDPCWrapper, pArgs);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&paExecCpuDpcs[i], HighImportance);
+ rc = rtMpNtSetTargetProcessorDpc(&paExecCpuDpcs[i], RTMpCpuIdFromSetIndex(i));
+ }
+ }
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pArgs);
+ return rc;
+ }
+
+ /*
+ * Raise the IRQL to DISPATCH_LEVEL so we can't be rescheduled to another cpu.
+ * KeInsertQueueDpc must also be executed at IRQL >= DISPATCH_LEVEL.
+ */
+ KIRQL oldIrql;
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+
+ /*
+ * We cannot do other than assume a 1:1 relationship between the
+ * affinity mask and the process despite the warnings in the docs.
+ * If someone knows a better way to get this done, please let bird know.
+ */
+ ASMCompilerBarrier(); /* paranoia */
+ if (enmCpuid == RT_NT_CPUID_SPECIFIC)
+ {
+ ASMAtomicIncS32(&pArgs->cRefs);
+ BOOLEAN fRc = KeInsertQueueDpc(&paExecCpuDpcs[0], 0, 0);
+ Assert(fRc); NOREF(fRc);
+ }
+ else if (enmCpuid == RT_NT_CPUID_PAIR)
+ {
+ ASMAtomicIncS32(&pArgs->cRefs);
+ BOOLEAN fRc = KeInsertQueueDpc(&paExecCpuDpcs[0], 0, 0);
+ Assert(fRc); NOREF(fRc);
+
+ ASMAtomicIncS32(&pArgs->cRefs);
+ fRc = KeInsertQueueDpc(&paExecCpuDpcs[1], 0, 0);
+ Assert(fRc); NOREF(fRc);
+ }
+ else
+ {
+ uint32_t iSelf = RTMpCurSetIndex();
+ for (uint32_t i = 0; i < cDpcsNeeded; i++)
+ {
+ if ( (i != iSelf)
+ && RTCpuSetIsMemberByIndex(&OnlineSet, i))
+ {
+ ASMAtomicIncS32(&pArgs->cRefs);
+ BOOLEAN fRc = KeInsertQueueDpc(&paExecCpuDpcs[i], 0, 0);
+ Assert(fRc); NOREF(fRc);
+ }
+ }
+ if (enmCpuid != RT_NT_CPUID_OTHERS)
+ pfnWorker(iSelf, pvUser1, pvUser2);
+ }
+
+ KeLowerIrql(oldIrql);
+
+ /*
+ * Flush all DPCs and wait for completion. (can take long!)
+ */
+ /** @todo Consider changing this to an active wait using some atomic inc/dec
+ * stuff (and check for the current cpu above in the specific case). */
+ /** @todo Seems KeFlushQueuedDpcs doesn't wait for the DPCs to be completely
+ * executed. Seen pArgs being freed while some CPU was using it before
+ * cRefs was added. */
+ if (g_pfnrtNtKeFlushQueuedDpcs)
+ g_pfnrtNtKeFlushQueuedDpcs();
+
+ if (pcHits)
+ *pcHits = pArgs->cHits;
+
+ /* Dereference the argument structure. */
+ int32_t cRefs = ASMAtomicDecS32(&pArgs->cRefs);
+ Assert(cRefs >= 0);
+ if (cRefs == 0)
+ RTMemFree(pArgs);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ if (g_pfnrtKeIpiGenericCall)
+ return rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnAllBroadcastIpiWrapper,
+ NIL_RTCPUID, NIL_RTCPUID, NULL);
+ return rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_ALL, NIL_RTCPUID, NIL_RTCPUID, NULL);
+}
+
+
+RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ if (g_pfnrtKeIpiGenericCall)
+ return rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnOthersBroadcastIpiWrapper,
+ NIL_RTCPUID, NIL_RTCPUID, NULL);
+ return rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_OTHERS, NIL_RTCPUID, NIL_RTCPUID, NULL);
+}
+
+
+RTDECL(int) RTMpOnPair(RTCPUID idCpu1, RTCPUID idCpu2, uint32_t fFlags, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ int rc;
+ AssertReturn(idCpu1 != idCpu2, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & RTMPON_F_VALID_MASK), VERR_INVALID_FLAGS);
+ if ((fFlags & RTMPON_F_CONCURRENT_EXEC) && !g_pfnrtKeIpiGenericCall)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Check that both CPUs are online before doing the broadcast call.
+ */
+ if ( RTMpIsCpuOnline(idCpu1)
+ && RTMpIsCpuOnline(idCpu2))
+ {
+ /*
+ * The broadcast IPI isn't quite as bad as it could have been, because
+ * it looks like windows doesn't synchronize CPUs on the way out, they
+ * seems to get back to normal work while the pair is still busy.
+ */
+ uint32_t cHits = 0;
+ if (g_pfnrtKeIpiGenericCall)
+ rc = rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnPairBroadcastIpiWrapper, idCpu1, idCpu2, &cHits);
+ else
+ rc = rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_PAIR, idCpu1, idCpu2, &cHits);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(cHits <= 2);
+ if (cHits == 2)
+ rc = VINF_SUCCESS;
+ else if (cHits == 1)
+ rc = VERR_NOT_ALL_CPUS_SHOWED;
+ else if (cHits == 0)
+ rc = VERR_CPU_OFFLINE;
+ else
+ rc = VERR_CPU_IPE_1;
+ }
+ }
+ /*
+ * A CPU must be present to be considered just offline.
+ */
+ else if ( RTMpIsCpuPresent(idCpu1)
+ && RTMpIsCpuPresent(idCpu2))
+ rc = VERR_CPU_OFFLINE;
+ else
+ rc = VERR_CPU_NOT_FOUND;
+ return rc;
+}
+
+
+RTDECL(bool) RTMpOnPairIsConcurrentExecSupported(void)
+{
+ return g_pfnrtKeIpiGenericCall != NULL;
+}
+
+
+/**
+ * Releases a reference to a RTMPNTONSPECIFICARGS heap allocation, freeing it
+ * when the last reference is released.
+ */
+DECLINLINE(void) rtMpNtOnSpecificRelease(PRTMPNTONSPECIFICARGS pArgs)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pArgs->cRefs);
+ AssertMsg(cRefs <= 1, ("cRefs=%#x\n", cRefs));
+ if (cRefs == 0)
+ RTMemFree(pArgs);
+}
+
+
+/**
+ * Wrapper between the native nt per-cpu callbacks and PFNRTWORKER
+ *
+ * @param Dpc DPC object
+ * @param DeferredContext Context argument specified by KeInitializeDpc
+ * @param SystemArgument1 Argument specified by KeInsertQueueDpc
+ * @param SystemArgument2 Argument specified by KeInsertQueueDpc
+ */
+static VOID rtMpNtOnSpecificDpcWrapper(IN PKDPC Dpc, IN PVOID DeferredContext,
+ IN PVOID SystemArgument1, IN PVOID SystemArgument2)
+{
+ PRTMPNTONSPECIFICARGS pArgs = (PRTMPNTONSPECIFICARGS)DeferredContext;
+ RT_NOREF3(Dpc, SystemArgument1, SystemArgument2);
+
+ ASMAtomicWriteBool(&pArgs->fExecuting, true);
+
+ pArgs->CallbackArgs.pfnWorker(RTMpCpuId(), pArgs->CallbackArgs.pvUser1, pArgs->CallbackArgs.pvUser2);
+
+ ASMAtomicWriteBool(&pArgs->fDone, true);
+ KeSetEvent(&pArgs->DoneEvt, 1 /*PriorityIncrement*/, FALSE /*Wait*/);
+
+ rtMpNtOnSpecificRelease(pArgs);
+}
+
+
+RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ /*
+ * Don't try mess with an offline CPU.
+ */
+ if (!RTMpIsCpuOnline(idCpu))
+ return !RTMpIsCpuPossible(idCpu)
+ ? VERR_CPU_NOT_FOUND
+ : VERR_CPU_OFFLINE;
+
+ /*
+ * Use the broadcast IPI routine if there are no more than two CPUs online,
+ * or if the current IRQL is unsuitable for KeWaitForSingleObject.
+ */
+ int rc;
+ uint32_t cHits = 0;
+ if ( g_pfnrtKeIpiGenericCall
+ && ( RTMpGetOnlineCount() <= 2
+ || KeGetCurrentIrql() > APC_LEVEL)
+ )
+ {
+ rc = rtMpCallUsingBroadcastIpi(pfnWorker, pvUser1, pvUser2, rtmpNtOnSpecificBroadcastIpiWrapper,
+ idCpu, NIL_RTCPUID, &cHits);
+ if (RT_SUCCESS(rc))
+ {
+ if (cHits == 1)
+ return VINF_SUCCESS;
+ rc = cHits == 0 ? VERR_CPU_OFFLINE : VERR_CPU_IPE_1;
+ }
+ return rc;
+ }
+
+#if 0
+ rc = rtMpCallUsingDpcs(pfnWorker, pvUser1, pvUser2, RT_NT_CPUID_SPECIFIC, idCpu, NIL_RTCPUID, &cHits);
+ if (RT_SUCCESS(rc))
+ {
+ if (cHits == 1)
+ return VINF_SUCCESS;
+ rc = cHits == 0 ? VERR_CPU_OFFLINE : VERR_CPU_IPE_1;
+ }
+ return rc;
+
+#else
+ /*
+ * Initialize the argument package and the objects within it.
+ * The package is referenced counted to avoid unnecessary spinning to
+ * synchronize cleanup and prevent stack corruption.
+ */
+ PRTMPNTONSPECIFICARGS pArgs = (PRTMPNTONSPECIFICARGS)RTMemAllocZ(sizeof(*pArgs));
+ if (!pArgs)
+ return VERR_NO_MEMORY;
+ pArgs->cRefs = 2;
+ pArgs->fExecuting = false;
+ pArgs->fDone = false;
+ pArgs->CallbackArgs.pfnWorker = pfnWorker;
+ pArgs->CallbackArgs.pvUser1 = pvUser1;
+ pArgs->CallbackArgs.pvUser2 = pvUser2;
+ pArgs->CallbackArgs.idCpu = idCpu;
+ pArgs->CallbackArgs.cHits = 0;
+ pArgs->CallbackArgs.cRefs = 2;
+ KeInitializeEvent(&pArgs->DoneEvt, SynchronizationEvent, FALSE /* not signalled */);
+ KeInitializeDpc(&pArgs->Dpc, rtMpNtOnSpecificDpcWrapper, pArgs);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&pArgs->Dpc, HighImportance);
+ rc = rtMpNtSetTargetProcessorDpc(&pArgs->Dpc, idCpu);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pArgs);
+ return rc;
+ }
+
+ /*
+ * Disable preemption while we check the current processor and inserts the DPC.
+ */
+ KIRQL bOldIrql;
+ KeRaiseIrql(DISPATCH_LEVEL, &bOldIrql);
+ ASMCompilerBarrier(); /* paranoia */
+
+ if (RTMpCpuId() == idCpu)
+ {
+ /* Just execute the callback on the current CPU. */
+ pfnWorker(idCpu, pvUser1, pvUser2);
+ KeLowerIrql(bOldIrql);
+
+ RTMemFree(pArgs);
+ return VINF_SUCCESS;
+ }
+
+ /* Different CPU, so queue it if the CPU is still online. */
+ if (RTMpIsCpuOnline(idCpu))
+ {
+ BOOLEAN fRc = KeInsertQueueDpc(&pArgs->Dpc, 0, 0);
+ Assert(fRc); NOREF(fRc);
+ KeLowerIrql(bOldIrql);
+
+ uint64_t const nsRealWaitTS = RTTimeNanoTS();
+
+ /*
+ * Wait actively for a while in case the CPU/thread responds quickly.
+ */
+ uint32_t cLoopsLeft = 0x20000;
+ while (cLoopsLeft-- > 0)
+ {
+ if (pArgs->fDone)
+ {
+ rtMpNtOnSpecificRelease(pArgs);
+ return VINF_SUCCESS;
+ }
+ ASMNopPause();
+ }
+
+ /*
+ * It didn't respond, so wait on the event object, poking the CPU if it's slow.
+ */
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -10000; /* 1ms */
+ NTSTATUS rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout);
+ if (rcNt == STATUS_SUCCESS)
+ {
+ rtMpNtOnSpecificRelease(pArgs);
+ return VINF_SUCCESS;
+ }
+
+ /* If it hasn't respondend yet, maybe poke it and wait some more. */
+ if (rcNt == STATUS_TIMEOUT)
+ {
+ if ( !pArgs->fExecuting
+ && ( g_pfnrtMpPokeCpuWorker == rtMpPokeCpuUsingHalRequestIpiW7Plus
+ || g_pfnrtMpPokeCpuWorker == rtMpPokeCpuUsingHalRequestIpiPreW7))
+ RTMpPokeCpu(idCpu);
+
+ Timeout.QuadPart = -1280000; /* 128ms */
+ rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout);
+ if (rcNt == STATUS_SUCCESS)
+ {
+ rtMpNtOnSpecificRelease(pArgs);
+ return VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Something weird is happening, try bail out.
+ */
+ if (KeRemoveQueueDpc(&pArgs->Dpc))
+ {
+ RTMemFree(pArgs); /* DPC was still queued, so we can return without further ado. */
+ LogRel(("RTMpOnSpecific(%#x): Not processed after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt));
+ }
+ else
+ {
+ /* DPC is running, wait a good while for it to complete. */
+ LogRel(("RTMpOnSpecific(%#x): Still running after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt));
+
+ Timeout.QuadPart = -30*1000*1000*10; /* 30 seconds */
+ rcNt = KeWaitForSingleObject(&pArgs->DoneEvt, Executive, KernelMode, FALSE /* Alertable */, &Timeout);
+ if (rcNt != STATUS_SUCCESS)
+ LogRel(("RTMpOnSpecific(%#x): Giving up on running worker after %llu ns: rcNt=%#x\n", idCpu, RTTimeNanoTS() - nsRealWaitTS, rcNt));
+ }
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ {
+ /* CPU is offline.*/
+ KeLowerIrql(bOldIrql);
+ rc = !RTMpIsCpuPossible(idCpu) ? VERR_CPU_NOT_FOUND : VERR_CPU_OFFLINE;
+ }
+
+ rtMpNtOnSpecificRelease(pArgs);
+ return rc;
+#endif
+}
+
+
+
+
+static VOID rtMpNtPokeCpuDummy(IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
+{
+ NOREF(Dpc);
+ NOREF(DeferredContext);
+ NOREF(SystemArgument1);
+ NOREF(SystemArgument2);
+}
+
+
+/** Callback used by rtMpPokeCpuUsingBroadcastIpi. */
+static ULONG_PTR rtMpIpiGenericCall(ULONG_PTR Argument)
+{
+ NOREF(Argument);
+ return 0;
+}
+
+
+/**
+ * RTMpPokeCpu worker that uses broadcast IPIs for doing the work.
+ *
+ * @returns VINF_SUCCESS
+ * @param idCpu The CPU identifier.
+ */
+int rtMpPokeCpuUsingBroadcastIpi(RTCPUID idCpu)
+{
+ NOREF(idCpu);
+ g_pfnrtKeIpiGenericCall(rtMpIpiGenericCall, 0);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * RTMpPokeCpu worker that uses the Windows 7 and later version of
+ * HalRequestIpip to get the job done.
+ *
+ * @returns VINF_SUCCESS
+ * @param idCpu The CPU identifier.
+ */
+int rtMpPokeCpuUsingHalRequestIpiW7Plus(RTCPUID idCpu)
+{
+ /* idCpu is an HAL processor index, so we can use it directly. */
+ PKAFFINITY_EX pTarget = (PKAFFINITY_EX)alloca(g_cbRtMpNtKaffinityEx);
+ pTarget->Size = g_cRtMpNtKaffinityExEntries; /* (just in case KeInitializeAffinityEx starts using it) */
+ g_pfnrtKeInitializeAffinityEx(pTarget);
+ g_pfnrtKeAddProcessorAffinityEx(pTarget, idCpu);
+
+ g_pfnrtHalRequestIpiW7Plus(0, pTarget);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * RTMpPokeCpu worker that uses the Vista and earlier version of HalRequestIpip
+ * to get the job done.
+ *
+ * @returns VINF_SUCCESS
+ * @param idCpu The CPU identifier.
+ */
+int rtMpPokeCpuUsingHalRequestIpiPreW7(RTCPUID idCpu)
+{
+ __debugbreak(); /** @todo this code needs testing!! */
+ KAFFINITY Target = 1;
+ Target <<= idCpu;
+ g_pfnrtHalRequestIpiPreW7(Target);
+ return VINF_SUCCESS;
+}
+
+
+int rtMpPokeCpuUsingFailureNotSupported(RTCPUID idCpu)
+{
+ NOREF(idCpu);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+int rtMpPokeCpuUsingDpc(RTCPUID idCpu)
+{
+ Assert(g_cRtMpNtMaxCpus > 0 && g_cRtMpNtMaxGroups > 0); /* init order */
+
+ /*
+ * APC fallback.
+ */
+ static KDPC s_aPokeDpcs[RTCPUSET_MAX_CPUS] = {{0}};
+ static bool s_fPokeDPCsInitialized = false;
+
+ if (!s_fPokeDPCsInitialized)
+ {
+ for (unsigned i = 0; i < g_cRtMpNtMaxCpus; i++)
+ {
+ KeInitializeDpc(&s_aPokeDpcs[i], rtMpNtPokeCpuDummy, NULL);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&s_aPokeDpcs[i], HighImportance);
+ int rc = rtMpNtSetTargetProcessorDpc(&s_aPokeDpcs[i], idCpu);
+ if (RT_FAILURE(rc) && rc != VERR_CPU_NOT_FOUND)
+ return rc;
+ }
+
+ s_fPokeDPCsInitialized = true;
+ }
+
+ /* Raise the IRQL to DISPATCH_LEVEL so we can't be rescheduled to another cpu.
+ KeInsertQueueDpc must also be executed at IRQL >= DISPATCH_LEVEL. */
+ KIRQL oldIrql;
+ KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
+
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&s_aPokeDpcs[idCpu], HighImportance);
+ g_pfnrtKeSetTargetProcessorDpc(&s_aPokeDpcs[idCpu], (int)idCpu);
+
+ /* Assuming here that high importance DPCs will be delivered immediately; or at least an IPI will be sent immediately.
+ Note! Not true on at least Vista & Windows 7 */
+ BOOLEAN fRet = KeInsertQueueDpc(&s_aPokeDpcs[idCpu], 0, 0);
+
+ KeLowerIrql(oldIrql);
+ return fRet == TRUE ? VINF_SUCCESS : VERR_ACCESS_DENIED /* already queued */;
+}
+
+
+RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
+{
+ if (!RTMpIsCpuOnline(idCpu))
+ return !RTMpIsCpuPossible(idCpu)
+ ? VERR_CPU_NOT_FOUND
+ : VERR_CPU_OFFLINE;
+ /* Calls rtMpPokeCpuUsingDpc, rtMpPokeCpuUsingHalRequestIpiW7Plus or rtMpPokeCpuUsingBroadcastIpi. */
+ return g_pfnrtMpPokeCpuWorker(idCpu);
+}
+
+
+RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
+{
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp
new file mode 100644
index 00000000..8a21e12a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp
@@ -0,0 +1,823 @@
+/* $Id: nt3fakes-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - NT 3.x fakes for NT 4.0 KPIs.
+ */
+
+/*
+ * 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 _IMAGE_NT_HEADERS RT_CONCAT(_IMAGE_NT_HEADERS,ARCH_BITS)
+#include "the-nt-kernel.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/dbg.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/x86.h>
+#include <iprt/formats/mz.h>
+#include <iprt/formats/pecoff.h>
+#include "internal-r0drv-nt.h"
+
+typedef uint32_t DWORD;
+#include <VerRsrc.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLASM(void) rtNt3InitSymbolsAssembly(void); /* in nt3fakesA-r0drv-nt.asm */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static uint32_t g_uNt3MajorVer = 3;
+static uint32_t g_uNt3MinorVer = 51;
+static uint32_t g_uNt3BuildNo = 1057;
+static bool g_fNt3Checked = false;
+static bool g_fNt3Smp = false; /**< Not reliable. */
+static bool volatile g_fNt3VersionInitialized = false;
+
+static uint8_t *g_pbNt3OsKrnl = (uint8_t *)UINT32_C(0x80100000);
+static uint32_t g_cbNt3OsKrnl = 0x300000;
+static uint8_t *g_pbNt3Hal = (uint8_t *)UINT32_C(0x80400000);
+static uint32_t g_cbNt3Hal = _512K;
+static bool volatile g_fNt3ModuleInfoInitialized = false;
+
+
+RT_C_DECLS_BEGIN
+/** @name KPIs we provide fallback implementations for.
+ *
+ * The assembly init routine will point the __imp_xxx variable to the NT
+ * implementation if available, using the fallback if not.
+ * @{ */
+decltype(PsGetVersion) *g_pfnrtPsGetVersion;
+decltype(ZwQuerySystemInformation) *g_pfnrtZwQuerySystemInformation;
+decltype(KeSetTimerEx) *g_pfnrtKeSetTimerEx;
+decltype(IoAttachDeviceToDeviceStack) *g_pfnrtIoAttachDeviceToDeviceStack;
+decltype(PsGetCurrentProcessId) *g_pfnrtPsGetCurrentProcessId;
+decltype(ZwYieldExecution) *g_pfnrtZwYieldExecution;
+decltype(ExAcquireFastMutex) *g_pfnrtExAcquireFastMutex;
+decltype(ExReleaseFastMutex) *g_pfnrtExReleaseFastMutex;
+/** @} */
+
+/** @name Fastcall optimizations not present in NT 3.1.
+ *
+ * We try resolve both the stdcall and fastcall variants and patch it up in
+ * assembly. The last four routines are in the hal.
+ *
+ * @{ */
+decltype(IofCompleteRequest) *g_pfnrtIofCompleteRequest;
+decltype(ObfDereferenceObject) *g_pfnrtObfDereferenceObject;
+decltype(IofCallDriver) *g_pfnrtIofCallDriver;
+decltype(KfAcquireSpinLock) *g_pfnrtKfAcquireSpinLock;
+decltype(KfReleaseSpinLock) *g_pfnrtKfReleaseSpinLock;
+decltype(KefAcquireSpinLockAtDpcLevel) *g_pfnrtKefAcquireSpinLockAtDpcLevel;
+decltype(KefReleaseSpinLockFromDpcLevel) *g_pfnrtKefReleaseSpinLockFromDpcLevel;
+decltype(KfLowerIrql) *g_pfnrtKfLowerIrql;
+decltype(KfRaiseIrql) *g_pfnrtKfRaiseIrql;
+
+VOID (__stdcall *g_pfnrtIoCompleteRequest)(PIRP, CCHAR);
+LONG_PTR (__stdcall *g_pfnrtObDereferenceObject)(PVOID);
+NTSTATUS (__stdcall *g_pfnrtIoCallDriver)(PDEVICE_OBJECT, PIRP);
+KIRQL (__stdcall *g_pfnrtKeAcquireSpinLock)(PKSPIN_LOCK);
+VOID (__stdcall *g_pfnrtKeReleaseSpinLock)(PKSPIN_LOCK, KIRQL);
+KIRQL (__stdcall *g_pfnrtKeAcquireSpinLockAtDpcLevel)(PKSPIN_LOCK);
+VOID (__stdcall *g_pfnrtKeReleaseSpinLockFromDpcLevel)(PKSPIN_LOCK);
+VOID (__stdcall *g_pfnrtKeLowerIrql)(KIRQL);
+KIRQL (__stdcall *g_pfnrtKeRaiseIrql)(KIRQL);
+/** @} */
+
+/** @name DATA exports and associated stuff
+ * @{ */
+/** Import address table entry for KeTickCount (defined in asm). */
+extern KSYSTEM_TIME *_imp__KeTickCount;
+/** @} */
+
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtR0Nt3InitModuleInfo(void);
+
+
+/**
+ * Converts a string to a number, stopping at the first non-digit.
+ *
+ * @returns The value
+ * @param ppwcValue Pointer to the string pointer variable. Updated.
+ * @param pcwcValue Pointer to the string length variable. Updated.
+ */
+static uint32_t rtR0Nt3StringToNum(PCRTUTF16 *ppwcValue, size_t *pcwcValue)
+{
+ uint32_t uValue = 0;
+ PCRTUTF16 pwcValue = *ppwcValue;
+ size_t cwcValue = *pcwcValue;
+
+ while (cwcValue > 0)
+ {
+ RTUTF16 uc = *pwcValue;
+ unsigned uDigit = (unsigned)uc - (unsigned)'0';
+ if (uDigit < (unsigned)10)
+ {
+ uValue *= 10;
+ uValue += uDigit;
+ }
+ else
+ break;
+ pwcValue++;
+ cwcValue--;
+ }
+
+ *ppwcValue = pwcValue;
+ *pcwcValue = cwcValue;
+ return uValue;
+}
+
+
+/**
+ * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
+ * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentVersion'
+ */
+static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentVersion(PWSTR pwszValueName, ULONG uValueType,
+ PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
+{
+ RT_NOREF(pwszValueName, pvEntryCtx);
+ if ( uValueType == REG_SZ
+ || uValueType == REG_EXPAND_SZ)
+ {
+ PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
+ size_t cwcValue = cbValue / sizeof(*pwcValue);
+ uint32_t uMajor = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
+ uint32_t uMinor = 0;
+ if (cwcValue > 1)
+ {
+ pwcValue++;
+ cwcValue--;
+ uMinor = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
+ }
+
+ if (uMajor >= 3)
+ {
+ g_uNt3MajorVer = uMajor;
+ g_uNt3MinorVer = uMinor;
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion found: uMajor=%u uMinor=%u\n", uMajor, uMinor);
+ *(uint32_t *)pvUser |= RT_BIT_32(0);
+ return STATUS_SUCCESS;
+ }
+
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
+ }
+ else
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
+ * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentBuildNumber'
+ */
+static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentBuildNumber(PWSTR pwszValueName, ULONG uValueType,
+ PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
+{
+ RT_NOREF(pwszValueName, pvEntryCtx);
+ if ( uValueType == REG_SZ
+ || uValueType == REG_EXPAND_SZ)
+ {
+ PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
+ size_t cwcValue = cbValue / sizeof(*pwcValue);
+ uint32_t uBuildNo = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
+
+ if (uBuildNo >= 100 && uBuildNo < _1M)
+ {
+ g_uNt3BuildNo = uBuildNo;
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber found: uBuildNo=%u\n", uBuildNo);
+ *(uint32_t *)pvUser |= RT_BIT_32(1);
+ return STATUS_SUCCESS;
+ }
+
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
+ }
+ else
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
+ * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentType'
+ */
+static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentType(PWSTR pwszValueName, ULONG uValueType,
+ PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
+{
+ RT_NOREF(pwszValueName, pvEntryCtx);
+ if ( uValueType == REG_SZ
+ || uValueType == REG_EXPAND_SZ)
+ {
+ PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
+ size_t cwcValue = cbValue / sizeof(*pwcValue);
+
+ int fSmp = -1;
+ if (cwcValue >= 12 && RTUtf16NICmpAscii(pwcValue, "Uniprocessor", 12) == 0)
+ {
+ cwcValue -= 12;
+ pwcValue += 12;
+ fSmp = 0;
+ }
+ else if (cwcValue >= 14 && RTUtf16NICmpAscii(pwcValue, "Multiprocessor", 14) == 0)
+ {
+ cwcValue -= 14;
+ pwcValue += 14;
+ fSmp = 1;
+ }
+ if (fSmp != -1)
+ {
+ while (cwcValue > 0 && RT_C_IS_SPACE(*pwcValue))
+ cwcValue--, pwcValue++;
+
+ int fChecked = -1;
+ if (cwcValue >= 4 && RTUtf16NICmpAscii(pwcValue, "Free", 4) == 0)
+ fChecked = 0;
+ else if (cwcValue >= 7 && RTUtf16NICmpAscii(pwcValue, "Checked", 7) == 0)
+ fChecked = 1;
+ if (fChecked != -1)
+ {
+ g_fNt3Smp = fSmp != 0;
+ g_fNt3Checked = fChecked != 0;
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType found: fSmp=%d fChecked=%d\n", fSmp, fChecked);
+ *(uint32_t *)pvUser |= RT_BIT_32(2);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
+ }
+ else
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Figure out the NT 3 version from the registry.
+ *
+ * @note this will be called before the rtR0Nt3InitSymbols is called.
+ */
+static void rtR0Nt3InitVersion(void)
+{
+ /*
+ * No PsGetVersion, so try the registry. Unfortunately not necessarily
+ * initialized when we're loaded.
+ */
+ RTL_QUERY_REGISTRY_TABLE aQuery[4];
+ RT_ZERO(aQuery);
+ aQuery[0].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentVersion;
+ aQuery[0].Flags = 0;
+ aQuery[0].Name = L"CurrentVersion";
+ aQuery[0].EntryContext = NULL;
+ aQuery[0].DefaultType = REG_NONE;
+
+ aQuery[1].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentBuildNumber;
+ aQuery[1].Flags = 0;
+ aQuery[1].Name = L"CurrentBuildNumber";
+ aQuery[1].EntryContext = NULL;
+ aQuery[1].DefaultType = REG_NONE;
+
+ aQuery[2].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentType;
+ aQuery[2].Flags = 0;
+ aQuery[2].Name = L"CurrentType";
+ aQuery[2].EntryContext = NULL;
+ aQuery[2].DefaultType = REG_NONE;
+
+ uint32_t fFound = 0;
+ //NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, NULL, &aQuery[0], &fFound, NULL /*Environment*/);
+ NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+ L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
+ &aQuery[0], &fFound, NULL /*Environment*/);
+ if (!NT_SUCCESS(rcNt))
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: RtlQueryRegistryValues failed: %#x\n", rcNt);
+ else
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: Didn't get all values: fFound=%#x\n", fFound);
+
+ /*
+ * We really need the version number. Build, type and SMP is off less importance.
+ * Derive it from the NT kernel PE header.
+ */
+ if (!(fFound & RT_BIT_32(0)))
+ {
+ if (!g_fNt3ModuleInfoInitialized)
+ rtR0Nt3InitModuleInfo();
+
+ PIMAGE_DOS_HEADER pMzHdr = (PIMAGE_DOS_HEADER)g_pbNt3OsKrnl;
+ PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)&g_pbNt3OsKrnl[pMzHdr->e_lfanew];
+ if (pNtHdrs->OptionalHeader.MajorOperatingSystemVersion == 1)
+ {
+ /* NT 3.1 and NT 3.50 both set OS version to 1.0 in the optional header. */
+ g_uNt3MajorVer = 3;
+ if ( pNtHdrs->OptionalHeader.MajorLinkerVersion == 2
+ && pNtHdrs->OptionalHeader.MinorLinkerVersion < 50)
+ g_uNt3MinorVer = 10;
+ else
+ g_uNt3MinorVer = 50;
+ }
+ else
+ {
+ g_uNt3MajorVer = pNtHdrs->OptionalHeader.MajorOperatingSystemVersion;
+ g_uNt3MinorVer = pNtHdrs->OptionalHeader.MinorOperatingSystemVersion;
+ }
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: guessed %u.%u from PE header\n", g_uNt3MajorVer, g_uNt3MinorVer);
+
+ /* Check out the resource section, looking for VS_FIXEDFILEINFO. */
+ __try /* (pointless) */
+ {
+ PIMAGE_SECTION_HEADER paShdrs = (PIMAGE_SECTION_HEADER)(pNtHdrs + 1);
+ uint32_t const cShdrs = pNtHdrs->FileHeader.NumberOfSections;
+ uint32_t iShdr = 0;
+ while (iShdr < cShdrs && memcmp(paShdrs[iShdr].Name, ".rsrc", 6) != 0)
+ iShdr++;
+ if (iShdr < cShdrs)
+ {
+ if ( paShdrs[iShdr].VirtualAddress > 0
+ && paShdrs[iShdr].VirtualAddress < pNtHdrs->OptionalHeader.SizeOfImage)
+ {
+ uint32_t const cbRsrc = RT_MIN(paShdrs[iShdr].Misc.VirtualSize
+ ? paShdrs[iShdr].Misc.VirtualSize : paShdrs[iShdr].SizeOfRawData,
+ pNtHdrs->OptionalHeader.SizeOfImage - paShdrs[iShdr].VirtualAddress);
+ uint8_t const *pbRsrc = &g_pbNt3OsKrnl[paShdrs[iShdr].VirtualAddress];
+ uint32_t const *puDwords = (uint32_t const *)pbRsrc;
+ uint32_t cDWords = (cbRsrc - sizeof(VS_FIXEDFILEINFO) + sizeof(uint32_t)) / sizeof(uint32_t);
+ while (cDWords-- > 0)
+ {
+ if ( puDwords[0] == VS_FFI_SIGNATURE
+ && puDwords[1] == VS_FFI_STRUCVERSION)
+ {
+ VS_FIXEDFILEINFO const *pVerInfo = (VS_FIXEDFILEINFO const *)puDwords;
+ g_uNt3MajorVer = pVerInfo->dwProductVersionMS >> 16;
+ g_uNt3MinorVer = pVerInfo->dwProductVersionMS >> 16;
+ g_uNt3BuildNo = pVerInfo->dwProductVersionLS >> 16;
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: Found version info %u.%u build %u\n",
+ g_uNt3MajorVer, g_uNt3MinorVer, g_uNt3BuildNo);
+ break;
+ }
+ puDwords++;
+ }
+ }
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: Exception scanning .rsrc section for version info!\n");
+ }
+ }
+
+ /*
+ * If we've got PsGetVersion, use it to override the above finding!
+ * (We may end up here for reasons other than the PsGetVersion fallback.)
+ */
+ if (g_pfnrtPsGetVersion)
+ {
+ WCHAR wszCsd[64];
+ UNICODE_STRING UniStr;
+ UniStr.Buffer = wszCsd;
+ UniStr.MaximumLength = sizeof(wszCsd) - sizeof(WCHAR);
+ UniStr.Length = 0;
+ RT_ZERO(wszCsd);
+ ULONG uMajor = 3;
+ ULONG uMinor = 51;
+ ULONG uBuildNo = 1057;
+ BOOLEAN fChecked = g_pfnrtPsGetVersion(&uMajor, &uMinor, &uBuildNo, &UniStr);
+
+ g_uNt3MajorVer = uMajor;
+ g_uNt3MinorVer = uMinor;
+ g_uNt3BuildNo = uBuildNo;
+ g_fNt3Checked = fChecked != FALSE;
+ }
+
+ g_fNt3VersionInitialized = true;
+}
+
+
+extern "C" DECLEXPORT(BOOLEAN) __stdcall
+Nt3Fb_PsGetVersion(ULONG *puMajor, ULONG *puMinor, ULONG *puBuildNo, UNICODE_STRING *pCsdStr)
+{
+ if (!g_fNt3VersionInitialized)
+ rtR0Nt3InitVersion();
+ if (puMajor)
+ *puMajor = g_uNt3MajorVer;
+ if (puMinor)
+ *puMinor = g_uNt3MinorVer;
+ if (puBuildNo)
+ *puBuildNo = g_uNt3BuildNo;
+ if (pCsdStr)
+ {
+ pCsdStr->Buffer[0] = '\0';
+ pCsdStr->Length = 0;
+ }
+ return g_fNt3Checked;
+}
+
+
+/**
+ * Worker for rtR0Nt3InitModuleInfo.
+ */
+static bool rtR0Nt3InitModuleInfoOne(const char *pszImage, uint8_t const *pbCode, uint8_t **ppbModule, uint32_t *pcbModule)
+{
+ uintptr_t const uImageAlign = _4K; /* XP may put the kernel at */
+
+ /* Align pbCode. */
+ pbCode = (uint8_t const *)((uintptr_t)pbCode & ~(uintptr_t)(uImageAlign - 1));
+
+ /* Scan backwards till we find a PE signature. */
+ for (uint32_t cbChecked = 0; cbChecked < _64M; cbChecked += uImageAlign, pbCode -= uImageAlign)
+ {
+ if (!MmIsAddressValid((void *)pbCode))
+ continue;
+
+ uint32_t uZero = 0;
+ uint32_t offNewHdr = 0;
+ __try /* pointless */
+ {
+ uZero = *(uint32_t const *)pbCode;
+ offNewHdr = *(uint32_t const *)&pbCode[RT_UOFFSETOF(IMAGE_DOS_HEADER, e_lfanew)];
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p scanning for DOS header...\n", pbCode);
+ continue;
+ }
+ if ( (uint16_t)uZero == IMAGE_DOS_SIGNATURE
+ && offNewHdr < _2K
+ && offNewHdr >= sizeof(IMAGE_DOS_HEADER))
+ {
+ RT_CONCAT(IMAGE_NT_HEADERS,ARCH_BITS) NtHdrs;
+ __try /* pointless */
+ {
+ NtHdrs = *(decltype(NtHdrs) const *)&pbCode[offNewHdr];
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p reading NT headers...\n", pbCode);
+ continue;
+ }
+ if ( NtHdrs.Signature == IMAGE_NT_SIGNATURE
+ && NtHdrs.FileHeader.SizeOfOptionalHeader == sizeof(NtHdrs.OptionalHeader)
+ && NtHdrs.FileHeader.NumberOfSections > 2
+ && NtHdrs.FileHeader.NumberOfSections < _4K
+ && NtHdrs.OptionalHeader.Magic == RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
+ {
+ *ppbModule = (uint8_t *)pbCode;
+ *pcbModule = NtHdrs.OptionalHeader.SizeOfImage;
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Found %s at %#p LB %#x\n",
+ pszImage, pbCode, NtHdrs.OptionalHeader.SizeOfImage);
+ return true;
+ }
+ }
+ }
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Warning! Unable to locate %s...\n");
+ return false;
+}
+
+
+/**
+ * Initializes the module information (NTOSKRNL + HAL) using exported symbols.
+ * This only works as long as noone is intercepting the symbols.
+ */
+static void rtR0Nt3InitModuleInfo(void)
+{
+ rtR0Nt3InitModuleInfoOne("ntoskrnl.exe", (uint8_t const *)(uintptr_t)IoGetCurrentProcess, &g_pbNt3OsKrnl, &g_cbNt3OsKrnl);
+ rtR0Nt3InitModuleInfoOne("hal.dll", (uint8_t const *)(uintptr_t)HalGetBusData, &g_pbNt3Hal, &g_cbNt3Hal);
+ g_fNt3ModuleInfoInitialized = true;
+}
+
+
+extern "C" DECLEXPORT(NTSTATUS) __stdcall
+Nt3Fb_ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS enmClass, PVOID pvBuf, ULONG cbBuf, PULONG pcbActual)
+{
+ switch (enmClass)
+ {
+ case SystemModuleInformation:
+ {
+ PRTL_PROCESS_MODULES pInfo = (PRTL_PROCESS_MODULES)pvBuf;
+ ULONG cbNeeded = RT_UOFFSETOF(RTL_PROCESS_MODULES, Modules[2]);
+ if (pcbActual)
+ *pcbActual = cbNeeded;
+ if (cbBuf < cbNeeded)
+ return STATUS_INFO_LENGTH_MISMATCH;
+
+ if (!g_fNt3ModuleInfoInitialized)
+ rtR0Nt3InitModuleInfo();
+
+ pInfo->NumberOfModules = 2;
+
+ /* ntoskrnl.exe */
+ pInfo->Modules[0].Section = NULL;
+ pInfo->Modules[0].MappedBase = g_pbNt3OsKrnl;
+ pInfo->Modules[0].ImageBase = g_pbNt3OsKrnl;
+ pInfo->Modules[0].ImageSize = g_cbNt3OsKrnl;
+ pInfo->Modules[0].Flags = 0;
+ pInfo->Modules[0].LoadOrderIndex = 0;
+ pInfo->Modules[0].InitOrderIndex = 0;
+ pInfo->Modules[0].LoadCount = 1024;
+ pInfo->Modules[0].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1;
+ memcpy(pInfo->Modules[0].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\ntoskrnl.exe"));
+
+ /* hal.dll */
+ pInfo->Modules[1].Section = NULL;
+ pInfo->Modules[1].MappedBase = g_pbNt3Hal;
+ pInfo->Modules[1].ImageBase = g_pbNt3Hal;
+ pInfo->Modules[1].ImageSize = g_cbNt3Hal;
+ pInfo->Modules[1].Flags = 0;
+ pInfo->Modules[1].LoadOrderIndex = 1;
+ pInfo->Modules[1].InitOrderIndex = 0;
+ pInfo->Modules[1].LoadCount = 1024;
+ pInfo->Modules[1].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1;
+ memcpy(pInfo->Modules[1].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\hal.dll"));
+
+ return STATUS_SUCCESS;
+ }
+
+ default:
+ return STATUS_INVALID_INFO_CLASS;
+ }
+}
+
+/**
+ * Calculates the length indicated by an ModR/M sequence.
+ *
+ * @returns Length, including RM byte.
+ * @param bRm The RM byte.
+ */
+static uint32_t rtR0Nt3CalcModRmLength(uint8_t bRm)
+{
+ uint32_t cbRm = 1;
+
+ if ( (bRm & X86_MODRM_MOD_MASK) == (3 << X86_MODRM_MOD_SHIFT)
+ || (bRm & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5)
+ cbRm += 4; /* disp32 */
+ else if ((bRm & X86_MODRM_MOD_MASK) == (1 << X86_MODRM_MOD_SHIFT))
+ cbRm += 1; /* disp8 */
+ else if ((bRm & X86_MODRM_MOD_MASK) == (2 << X86_MODRM_MOD_SHIFT))
+ cbRm += 2; /* disp16 */
+
+ if ((bRm & X86_MODRM_RM_MASK) == 4 && (bRm & X86_MODRM_MOD_MASK) != (3 << X86_MODRM_MOD_SHIFT))
+ cbRm += 1; /* SIB */
+
+ return cbRm;
+}
+
+
+/**
+ * Init symbols.
+ *
+ * This is called after both ZwQuerySystemInformation and PsGetVersion are used
+ * for the first time.
+ *
+ * @returns IPRT status code
+ * @param hKrnlInfo Kernel symbol digger handle.
+ */
+DECLHIDDEN(int) rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo)
+{
+ /*
+ * Resolve symbols. (We set C variables (g_pfnrtXxx) here, not the __imp__Xxx ones.)
+ */
+#define GET_SYSTEM_ROUTINE(a_fnName) do { \
+ RT_CONCAT(g_pfnrt, a_fnName) = (decltype(RT_CONCAT(g_pfnrt, a_fnName)))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, #a_fnName); \
+ } while (0)
+
+ GET_SYSTEM_ROUTINE(PsGetVersion);
+ GET_SYSTEM_ROUTINE(ZwQuerySystemInformation);
+ GET_SYSTEM_ROUTINE(KeSetTimerEx);
+ GET_SYSTEM_ROUTINE(IoAttachDeviceToDeviceStack);
+ GET_SYSTEM_ROUTINE(PsGetCurrentProcessId);
+ GET_SYSTEM_ROUTINE(ZwYieldExecution);
+ GET_SYSTEM_ROUTINE(ExAcquireFastMutex);
+ GET_SYSTEM_ROUTINE(ExReleaseFastMutex);
+
+#define GET_FAST_CALL_SYSTEM_ROUTINE(a_fnFastcall, a_fnStdcall) do { \
+ GET_SYSTEM_ROUTINE(a_fnFastcall); \
+ GET_SYSTEM_ROUTINE(a_fnStdcall); \
+ AssertLogRelReturn(RT_CONCAT(g_pfnrt,a_fnFastcall) || RT_CONCAT(g_pfnrt,a_fnStdcall), VERR_INTERNAL_ERROR_3); \
+ } while (0)
+ GET_FAST_CALL_SYSTEM_ROUTINE(IofCompleteRequest, IoCompleteRequest);
+ GET_FAST_CALL_SYSTEM_ROUTINE(ObfDereferenceObject, ObDereferenceObject);
+ GET_FAST_CALL_SYSTEM_ROUTINE(IofCallDriver, IoCallDriver);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfAcquireSpinLock, KeAcquireSpinLock);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfReleaseSpinLock, KeReleaseSpinLock);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfLowerIrql, KeLowerIrql);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfRaiseIrql, KeRaiseIrql);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KefAcquireSpinLockAtDpcLevel, KeAcquireSpinLockAtDpcLevel);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KefReleaseSpinLockFromDpcLevel, KeReleaseSpinLockFromDpcLevel);
+
+ /*
+ * We need to call assembly to update the __imp__Xxx entries, since C
+ * doesn't allow '@' in symbols.
+ */
+ rtNt3InitSymbolsAssembly();
+
+ /*
+ * Tick count data. We disassemble KeQueryTickCount until we find the
+ * first absolute address referenced in it.
+ * %80105b70 8b 44 24 04 mov eax, dword [esp+004h]
+ * %80105b74 c7 40 04 00 00 00 00 mov dword [eax+004h], 000000000h
+ * %80105b7b 8b 0d 88 70 19 80 mov ecx, dword [080197088h]
+ * %80105b81 89 08 mov dword [eax], ecx
+ * %80105b83 c2 04 00 retn 00004h
+ */
+ _imp__KeTickCount = (decltype(_imp__KeTickCount))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeTickCount");
+ if (!_imp__KeTickCount)
+ {
+ if (!g_fNt3VersionInitialized)
+ rtR0Nt3InitVersion();
+ Assert(g_uNt3MajorVer == 3 && g_uNt3MinorVer < 50);
+
+ uint8_t const *pbCode = (uint8_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeQueryTickCount");
+ AssertLogRelReturn(pbCode, VERR_INTERNAL_ERROR_2);
+
+ for (uint32_t off = 0; off < 128 && _imp__KeTickCount == NULL;)
+ {
+ uint8_t const b1 = pbCode[off++];
+ switch (b1)
+ {
+ case 0x8b: /* mov reg, r/m ; We're looking for absolute address in r/m. */
+ if ((pbCode[off] & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5 /*disp32*/)
+ _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off + 1];
+ RT_FALL_THRU();
+ case 0x89: /* mov r/m, reg */
+ off += rtR0Nt3CalcModRmLength(pbCode[off]);
+ break;
+
+ case 0xc7:
+ if ((pbCode[off] & X86_MODRM_REG_MASK) == 0) /* mov r/m, imm32 */
+ off += rtR0Nt3CalcModRmLength(pbCode[off]) + 4;
+ else
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n",
+ off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode);
+ return VERR_INTERNAL_ERROR_3;
+ }
+ break;
+
+ case 0xc2: /* ret iw */
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered RET! %.*Rhxs\n",
+ off + 2, pbCode);
+ return VERR_INTERNAL_ERROR_3;
+
+ default:
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n",
+ off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode);
+ return VERR_INTERNAL_ERROR_3;
+
+ /* Just in case: */
+
+ case 0xa1: /* mov eax, [m32] */
+ _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off];
+ off += 4;
+ break;
+
+ case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: /* push reg */
+ break;
+ }
+ }
+ if (!_imp__KeTickCount)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount after 128 bytes! %.*Rhxs\n", 128, pbCode);
+ return VERR_INTERNAL_ERROR_3;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+extern "C" DECLEXPORT(VOID)
+Nt3Fb_KeInitializeTimerEx(PKTIMER pTimer, TIMER_TYPE enmType)
+{
+ KeInitializeTimer(pTimer);
+ NOREF(enmType);
+ /** @todo Default is NotificationTimer, for SyncrhonizationTimer we need to
+ * do more work. timer-r0drv-nt.cpp is using the latter. :/ */
+}
+
+
+extern "C" DECLEXPORT(BOOLEAN) __stdcall
+Nt3Fb_KeSetTimerEx(PKTIMER pTimer, LARGE_INTEGER DueTime, LONG cMsPeriod, PKDPC pDpc)
+{
+ AssertReturn(cMsPeriod == 0, FALSE);
+ return KeSetTimer(pTimer, DueTime, pDpc);
+}
+
+
+extern "C" DECLEXPORT(PDEVICE_OBJECT)
+Nt3Fb_IoAttachDeviceToDeviceStack(PDEVICE_OBJECT pSourceDevice, PDEVICE_OBJECT pTargetDevice)
+{
+ NOREF(pSourceDevice); NOREF(pTargetDevice);
+ return NULL;
+}
+
+
+extern "C" DECLEXPORT(HANDLE)
+Nt3Fb_PsGetCurrentProcessId(void)
+{
+ if (!g_fNt3VersionInitialized)
+ rtR0Nt3InitVersion();
+
+ uint8_t const *pbProcess = (uint8_t const *)IoGetCurrentProcess();
+ if ( g_uNt3MajorVer > 3
+ || g_uNt3MinorVer >= 50)
+ return *(HANDLE const *)&pbProcess[0x94];
+ return *(HANDLE const *)&pbProcess[0xb0];
+}
+
+
+extern "C" DECLEXPORT(NTSTATUS)
+Nt3Fb_ZwYieldExecution(VOID)
+{
+ LARGE_INTEGER Interval;
+ Interval.QuadPart = 0;
+ KeDelayExecutionThread(KernelMode, FALSE, &Interval);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * This is a simple implementation of the fast mutex api introduced in 3.50.
+ */
+extern "C" DECLEXPORT(VOID) FASTCALL
+Nt3Fb_ExAcquireFastMutex(PFAST_MUTEX pFastMtx)
+{
+ PETHREAD pSelf = PsGetCurrentThread();
+ KIRQL OldIrql;
+ KeRaiseIrql(APC_LEVEL, &OldIrql);
+
+ /* The Count member is initialized to 1. So if we decrement it to zero, we're
+ the first locker and owns the mutex. Otherwise we must wait for our turn. */
+ int32_t cLockers = ASMAtomicDecS32((int32_t volatile *)&pFastMtx->Count);
+ if (cLockers != 0)
+ {
+ ASMAtomicIncU32((uint32_t volatile *)&pFastMtx->Contention);
+ KeWaitForSingleObject(&pFastMtx->Event, Executive, KernelMode, FALSE /*fAlertable*/, NULL /*pTimeout*/);
+ }
+
+ pFastMtx->Owner = (PKTHREAD)pSelf;
+ pFastMtx->OldIrql = OldIrql;
+}
+
+
+/**
+ * This is a simple implementation of the fast mutex api introduced in 3.50.
+ */
+extern "C" DECLEXPORT(VOID) FASTCALL
+Nt3Fb_ExReleaseFastMutex(PFAST_MUTEX pFastMtx)
+{
+ AssertMsg(pFastMtx->Owner == (PKTHREAD)PsGetCurrentThread(), ("Owner=%p, expected %p\n", pFastMtx->Owner, PsGetCurrentThread()));
+
+ KIRQL OldIrql = pFastMtx->OldIrql;
+ pFastMtx->Owner = NULL;
+ int32_t cLockers = ASMAtomicIncS32((int32_t volatile *)&pFastMtx->Count);
+ if (cLockers <= 0)
+ KeSetEvent(&pFastMtx->Event, EVENT_INCREMENT, FALSE /*fWait*/);
+ if (OldIrql != APC_LEVEL)
+ KeLowerIrql(OldIrql);
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp
new file mode 100644
index 00000000..b23ec713
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/nt3fakes-stub-r0drv-nt.cpp
@@ -0,0 +1,52 @@
+/* $Id: nt3fakes-stub-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - NT 3.x fakes for NT 4.0+ KPIs, init stub.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+#include <iprt/errcore.h>
+#include <iprt/dbg.h>
+#include "internal-r0drv-nt.h"
+
+
+int rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo)
+{
+ NOREF(hKrnlInfo);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm b/src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm
new file mode 100644
index 00000000..d0ed3199
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/nt3fakesA-r0drv-nt.asm
@@ -0,0 +1,157 @@
+; $Id: nt3fakesA-r0drv-nt.asm $
+;; @file
+; IPRT - Companion to nt3fakes-r0drv-nt.cpp that provides import stuff to satisfy the linker.
+;
+
+;
+; 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"
+
+%undef NAME
+%define NAME(name) NAME_OVERLOAD(name)
+
+BEGINCODE
+
+;;
+; Called from rtR0Nt3InitSymbols after symbols have been resolved.
+BEGINPROC _rtNt3InitSymbolsAssembly
+ push ebp
+ mov ebp, esp
+
+;;
+; @param 1 The fastcall name.
+; @param 2 Byte size of arguments.
+%macro DefineImportDataAndInitCode 3
+extern $%1 %+ Nt3Fb_ %+ %2 %+ @ %+ %3
+BEGINDATA
+extern _g_pfnrt %+ %2
+GLOBALNAME __imp_ %+ %1 %+ %2 %+ @ %+ %3
+ dd $%1 %+ Nt3Fb_ %+ %2 %+ @ %+ %3
+BEGINCODE
+ mov eax, [_g_pfnrt %+ %2]
+ test eax, eax
+ jz %%next
+ mov [__imp_ %+ %1 %+ %2 %+ @ %+ %3], eax
+%%next:
+%endmacro
+
+ DefineImportDataAndInitCode _,PsGetVersion, 16
+ DefineImportDataAndInitCode _,ZwQuerySystemInformation, 16
+ DefineImportDataAndInitCode _,KeSetTimerEx, 20
+ DefineImportDataAndInitCode _,IoAttachDeviceToDeviceStack, 8
+ DefineImportDataAndInitCode _,PsGetCurrentProcessId, 0
+ DefineImportDataAndInitCode _,ZwYieldExecution, 0
+ DefineImportDataAndInitCode @,ExAcquireFastMutex, 4
+ DefineImportDataAndInitCode @,ExReleaseFastMutex, 4
+
+ xor eax, eax
+ leave
+ ret
+ENDPROC _rtNt3InitSymbolsAssembly
+
+
+;;
+; @param 1 The fastcall name.
+; @param 2 The stdcall name.
+; @param 3 Byte size of arguments.
+; @param 4 Zero if 1:1 mapping;
+; One if 2nd parameter is a byte pointer that the farcall version
+; instead returns in al.
+%macro FastOrStdCallWrapper 4
+BEGINCODE
+extern _g_pfnrt %+ %1
+extern _g_pfnrt %+ %2
+BEGINPROC_EXPORTED $@ %+ %1 %+ @ %+ %3
+ mov eax, [_g_pfnrt %+ %1]
+ cmp eax, 0
+ jnz .got_fast_call
+ mov eax, .stdcall_wrapper
+ mov [__imp_@ %+ %1 %+ @ %+ %3], eax
+
+.stdcall_wrapper:
+ push ebp
+ mov ebp, esp
+%if %4 == 1
+ push dword 0
+ push esp
+%else
+ push edx
+%endif
+ push ecx
+ call [_g_pfnrt %+ %2]
+%if %4 == 1
+ movzx eax, byte [ebp - 4]
+%endif
+ leave
+ ret
+
+.got_fast_call:
+ mov [__imp_@ %+ %1 %+ @ %+ %3], eax
+ jmp eax
+ENDPROC $@ %+ %1 %+ @ %+ %3
+
+BEGINDATA
+GLOBALNAME __imp_@ %+ %1 %+ @ %+ %3
+ dd $@ %+ %1 %+ @ %+ %3
+%endmacro
+
+FastOrStdCallWrapper IofCompleteRequest, IoCompleteRequest, 8, 0
+FastOrStdCallWrapper IofCallDriver, IoCallDriver, 8, 0
+FastOrStdCallWrapper ObfDereferenceObject, ObDereferenceObject, 4, 0
+FastOrStdCallWrapper KfAcquireSpinLock, KeAcquireSpinLock, 4, 1
+FastOrStdCallWrapper KfReleaseSpinLock, KeReleaseSpinLock, 8, 0
+FastOrStdCallWrapper KfRaiseIrql, KeRaiseIrql, 4, 1
+FastOrStdCallWrapper KfLowerIrql, KeLowerIrql, 4, 0
+FastOrStdCallWrapper KefAcquireSpinLockAtDpcLevel, KeAcquireSpinLockAtDpcLevel, 4, 0
+FastOrStdCallWrapper KefReleaseSpinLockFromDpcLevel,KeReleaseSpinLockFromDpcLevel, 4, 0
+
+
+BEGINCODE
+; LONG FASTCALL InterlockedExchange(LONG volatile *,LONG );
+BEGINPROC_EXPORTED $@InterlockedExchange@8
+ mov eax, edx
+ xchg [ecx], eax
+ ret
+
+BEGINDATA
+GLOBALNAME __imp_@InterlockedExchange@8
+ dd $@InterlockedExchange@8
+
+
+BEGINDATA
+GLOBALNAME __imp__KeTickCount
+GLOBALNAME _KeTickCount
+ dd 0
+
diff --git a/src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp b/src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp
new file mode 100644
index 00000000..9bb46e99
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/ntBldSymDb.cpp
@@ -0,0 +1,1232 @@
+/* $Id: ntBldSymDb.cpp $ */
+/** @file
+ * IPRT - RTDirCreateUniqueNumbered, generic implementation.
+ */
+
+/*
+ * Copyright (C) 2013-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/dbghelp.h>
+
+#include <iprt/alloca.h>
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "r0drv/nt/symdb.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** A structure member we're interested in. */
+typedef struct MYMEMBER
+{
+ /** The member name. */
+ const char * const pszName;
+ /** Reserved. */
+ uint32_t const fFlags;
+ /** The offset of the member. UINT32_MAX if not found. */
+ uint32_t off;
+ /** The size of the member. */
+ uint32_t cb;
+ /** Alternative names, optional.
+ * This is a string of zero terminated strings, ending with an zero length
+ * string (or double '\\0' if you like). */
+ const char * const pszzAltNames;
+} MYMEMBER;
+/** Pointer to a member we're interested. */
+typedef MYMEMBER *PMYMEMBER;
+
+/** Members we're interested in. */
+typedef struct MYSTRUCT
+{
+ /** The structure name. */
+ const char * const pszName;
+ /** Array of members we're interested in. */
+ MYMEMBER *paMembers;
+ /** The number of members we're interested in. */
+ uint32_t const cMembers;
+ /** Reserved. */
+ uint32_t const fFlags;
+} MYSTRUCT;
+
+/** Architecture. */
+typedef enum MYARCH
+{
+ MYARCH_X86,
+ MYARCH_AMD64,
+ MYARCH_DETECT
+} MYARCH;
+
+/** Set of structures for one kernel. */
+typedef struct MYSET
+{
+ /** The list entry. */
+ RTLISTNODE ListEntry;
+ /** The source PDB. */
+ char *pszPdb;
+ /** The OS version we've harvested structs for */
+ RTNTSDBOSVER OsVerInfo;
+ /** The architecture. */
+ MYARCH enmArch;
+ /** The structures and their member. */
+ MYSTRUCT aStructs[1];
+} MYSET;
+/** Pointer a set of structures for one kernel. */
+typedef MYSET *PMYSET;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Verbosity level (-v, --verbose). */
+static uint32_t g_iOptVerbose = 1;
+/** Set if we should force ahead despite errors. */
+static bool g_fOptForce = false;
+
+/** The members of the KPRCB structure that we're interested in. */
+static MYMEMBER g_aKprcbMembers[] =
+{
+ { "QuantumEnd", 0, UINT32_MAX, UINT32_MAX, NULL },
+ { "DpcQueueDepth", 0, UINT32_MAX, UINT32_MAX, "DpcData[0].DpcQueueDepth\0" },
+ { "VendorString", 0, UINT32_MAX, UINT32_MAX, NULL },
+};
+
+/** The structures we're interested in. */
+static MYSTRUCT g_aStructs[] =
+{
+ { "_KPRCB", &g_aKprcbMembers[0], RT_ELEMENTS(g_aKprcbMembers), 0 },
+};
+
+/** List of data we've found. This is sorted by version info. */
+static RTLISTANCHOR g_SetList;
+
+
+
+
+
+/**
+ * For debug/verbose output.
+ *
+ * @param pszFormat The format string.
+ * @param ... The arguments referenced in the format string.
+ */
+static void MyDbgPrintf(const char *pszFormat, ...)
+{
+ if (g_iOptVerbose > 1)
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ RTPrintf("debug: ");
+ RTPrintfV(pszFormat, va);
+ va_end(va);
+ }
+}
+
+
+/**
+ * Returns the name we wish to use in the C code.
+ * @returns Structure name.
+ * @param pStruct The structure descriptor.
+ */
+static const char *figureCStructName(MYSTRUCT const *pStruct)
+{
+ const char *psz = pStruct->pszName;
+ while (*psz == '_')
+ psz++;
+ return psz;
+}
+
+
+/**
+ * Returns the name we wish to use in the C code.
+ * @returns Member name.
+ * @param pMember The member descriptor.
+ */
+static const char *figureCMemberName(MYMEMBER const *pMember)
+{
+ return pMember->pszName;
+}
+
+
+/**
+ * Creates a MYSET with copies of all the data and inserts it into the
+ * g_SetList in a orderly fashion.
+ *
+ * @param pOut The output stream.
+ */
+static void generateHeader(PRTSTREAM pOut)
+{
+ RTStrmPrintf(pOut,
+ "/* $" "I" "d" ": $ */\n" /* avoid it being expanded */
+ "/** @file\n"
+ " * IPRT - NT kernel type helpers - Autogenerated, do NOT edit.\n"
+ " */\n"
+ "\n"
+ "/*\n"
+ " * Copyright (C) 2013-2023 Oracle and/or its affiliates.\n"
+ " *\n"
+ " * This file is part of VirtualBox base platform packages, as\n"
+ " * available from https://www.virtualbox.org.\n"
+ " *\n"
+ " * This program is free software; you can redistribute it and/or\n"
+ " * modify it under the terms of the GNU General Public License\n"
+ " * as published by the Free Software Foundation, in version 3 of the\n"
+ " * License.\n"
+ " *\n"
+ " * This program is distributed in the hope that it will be useful, but\n"
+ " * WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
+ " * General Public License for more details.\n"
+ " *\n"
+ " * You should have received a copy of the GNU General Public License\n"
+ " * along with this program; if not, see <https://www.gnu.org/licenses>.\n"
+ " *\n"
+ " * The contents of this file may alternatively be used under the terms\n"
+ " * of the Common Development and Distribution License Version 1.0\n"
+ " * (CDDL), a copy of it is provided in the \"COPYING.CDDL\" file included\n"
+ " * in the VirtualBox distribution, in which case the provisions of the\n"
+ " * CDDL are applicable instead of those of the GPL.\n"
+ " *\n"
+ " * You may elect to license modified versions of this file under the\n"
+ " * terms and conditions of either the GPL or the CDDL or both.\n"
+ " *\n"
+ " * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0\n"
+ " */\n"
+ "\n"
+ "\n"
+ "#ifndef IPRT_INCLUDED_SRC_nt_symdbdata_h\n"
+ "#define IPRT_INCLUDED_SRC_nt_symdbdata_h\n"
+ "\n"
+ "#include \"r0drv/nt/symdb.h\"\n"
+ "\n"
+ );
+
+ /*
+ * Generate types.
+ */
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
+ {
+ const char *pszStructName = figureCStructName(&g_aStructs[i]);
+
+ RTStrmPrintf(pOut,
+ "typedef struct RTNTSDBTYPE_%s\n"
+ "{\n",
+ pszStructName);
+ PMYMEMBER paMembers = g_aStructs[i].paMembers;
+ for (uint32_t j = 0; j < g_aStructs->cMembers; j++)
+ {
+ const char *pszMemName = figureCMemberName(&paMembers[j]);
+ RTStrmPrintf(pOut,
+ " uint32_t off%s;\n"
+ " uint32_t cb%s;\n",
+ pszMemName, pszMemName);
+ }
+
+ RTStrmPrintf(pOut,
+ "} RTNTSDBTYPE_%s;\n"
+ "\n",
+ pszStructName);
+ }
+
+ RTStrmPrintf(pOut,
+ "\n"
+ "typedef struct RTNTSDBSET\n"
+ "{\n"
+ " RTNTSDBOSVER%-20s OsVerInfo;\n", "");
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
+ {
+ const char *pszStructName = figureCStructName(&g_aStructs[i]);
+ RTStrmPrintf(pOut, " RTNTSDBTYPE_%-20s %s;\n", pszStructName, pszStructName);
+ }
+ RTStrmPrintf(pOut,
+ "} RTNTSDBSET;\n"
+ "typedef RTNTSDBSET const *PCRTNTSDBSET;\n"
+ "\n");
+
+ /*
+ * Output the data.
+ */
+ RTStrmPrintf(pOut,
+ "\n"
+ "#ifndef RTNTSDB_NO_DATA\n"
+ "const RTNTSDBSET g_artNtSdbSets[] = \n"
+ "{\n");
+ PMYSET pSet;
+ RTListForEach(&g_SetList, pSet, MYSET, ListEntry)
+ {
+ const char *pszArch = pSet->enmArch == MYARCH_AMD64 ? "AMD64" : "X86";
+ RTStrmPrintf(pOut,
+ "# ifdef RT_ARCH_%s\n"
+ " { /* Source: %s */\n"
+ " /*.OsVerInfo = */\n"
+ " {\n"
+ " /* .uMajorVer = */ %u,\n"
+ " /* .uMinorVer = */ %u,\n"
+ " /* .fChecked = */ %s,\n"
+ " /* .fSmp = */ %s,\n"
+ " /* .uCsdNo = */ %u,\n"
+ " /* .uBuildNo = */ %u,\n"
+ " },\n",
+ pszArch,
+ pSet->pszPdb,
+ pSet->OsVerInfo.uMajorVer,
+ pSet->OsVerInfo.uMinorVer,
+ pSet->OsVerInfo.fChecked ? "true" : "false",
+ pSet->OsVerInfo.fSmp ? "true" : "false",
+ pSet->OsVerInfo.uCsdNo,
+ pSet->OsVerInfo.uBuildNo);
+ for (uint32_t i = 0; i < RT_ELEMENTS(pSet->aStructs); i++)
+ {
+ const char *pszStructName = figureCStructName(&pSet->aStructs[i]);
+ RTStrmPrintf(pOut,
+ " /* .%s = */\n"
+ " {\n", pszStructName);
+ PMYMEMBER paMembers = pSet->aStructs[i].paMembers;
+ for (uint32_t j = 0; j < pSet->aStructs[i].cMembers; j++)
+ {
+ const char *pszMemName = figureCMemberName(&paMembers[j]);
+ RTStrmPrintf(pOut,
+ " /* .off%-25s = */ %#06x,\n"
+ " /* .cb%-26s = */ %#06x,\n",
+ pszMemName, paMembers[j].off,
+ pszMemName, paMembers[j].cb);
+ }
+ RTStrmPrintf(pOut,
+ " },\n");
+ }
+ RTStrmPrintf(pOut,
+ " },\n"
+ "# endif\n"
+ );
+ }
+
+ RTStrmPrintf(pOut,
+ "};\n"
+ "#endif /* !RTNTSDB_NO_DATA */\n"
+ "\n");
+
+ RTStrmPrintf(pOut, "\n#endif\n\n");
+}
+
+
+/**
+ * Creates a MYSET with copies of all the data and inserts it into the
+ * g_SetList in a orderly fashion.
+ *
+ * @returns Fully complained exit code.
+ * @param pOsVerInfo The OS version info.
+ * @param enmArch The NT architecture of the incoming PDB.
+ * @param pszPdb The PDB file name.
+ */
+static RTEXITCODE saveStructures(PRTNTSDBOSVER pOsVerInfo, MYARCH enmArch, const char *pszPdb)
+{
+ /*
+ * Allocate one big chunk, figure it's size once.
+ */
+ static size_t s_cbNeeded = 0;
+ if (s_cbNeeded == 0)
+ {
+ s_cbNeeded = RT_UOFFSETOF(MYSET, aStructs[RT_ELEMENTS(g_aStructs)]);
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
+ s_cbNeeded += sizeof(MYMEMBER) * g_aStructs[i].cMembers;
+ }
+
+ size_t cbPdb = strlen(pszPdb) + 1;
+ PMYSET pSet = (PMYSET)RTMemAlloc(s_cbNeeded + cbPdb);
+ if (!pSet)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory!\n");
+
+ /*
+ * Copy over the data.
+ */
+ pSet->enmArch = enmArch;
+ memcpy(&pSet->OsVerInfo, pOsVerInfo, sizeof(pSet->OsVerInfo));
+ memcpy(&pSet->aStructs[0], g_aStructs, sizeof(g_aStructs));
+
+ PMYMEMBER pDst = (PMYMEMBER)&pSet->aStructs[RT_ELEMENTS(g_aStructs)];
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
+ {
+ pSet->aStructs[i].paMembers = pDst;
+ memcpy(pDst, g_aStructs[i].paMembers, g_aStructs[i].cMembers * sizeof(*pDst));
+ pDst += g_aStructs[i].cMembers;
+ }
+
+ pSet->pszPdb = (char *)pDst;
+ memcpy(pDst, pszPdb, cbPdb);
+
+ /*
+ * Link it.
+ */
+ PMYSET pInsertBefore;
+ RTListForEach(&g_SetList, pInsertBefore, MYSET, ListEntry)
+ {
+ int iDiff = rtNtOsVerInfoCompare(&pInsertBefore->OsVerInfo, &pSet->OsVerInfo);
+ if (iDiff >= 0)
+ {
+ if (iDiff > 0 || pInsertBefore->enmArch > pSet->enmArch)
+ {
+ RTListNodeInsertBefore(&pInsertBefore->ListEntry, &pSet->ListEntry);
+ return RTEXITCODE_SUCCESS;
+ }
+ }
+ }
+
+ RTListAppend(&g_SetList, &pSet->ListEntry);
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Checks that we found everything.
+ *
+ * @returns Fully complained exit code.
+ */
+static RTEXITCODE checkThatWeFoundEverything(void)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
+ {
+ PMYMEMBER paMembers = g_aStructs[i].paMembers;
+ uint32_t j = g_aStructs[i].cMembers;
+ while (j-- > 0)
+ {
+ if (paMembers[j].off == UINT32_MAX)
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, " Missing %s::%s\n", g_aStructs[i].pszName, paMembers[j].pszName);
+ }
+ }
+ return rcExit;
+}
+
+
+/**
+ * Matches the member against what we're looking for.
+ *
+ * @returns Number of hits.
+ * @param cWantedMembers The number members in paWantedMembers.
+ * @param paWantedMembers The members we're looking for.
+ * @param pszPrefix The member name prefix.
+ * @param pszMember The member name.
+ * @param offMember The member offset.
+ * @param cbMember The member size.
+ */
+static uint32_t matchUpStructMembers(unsigned cWantedMembers, PMYMEMBER paWantedMembers,
+ const char *pszPrefix, const char *pszMember,
+ uint32_t offMember, uint32_t cbMember)
+{
+ size_t cchPrefix = strlen(pszPrefix);
+ uint32_t cHits = 0;
+ uint32_t iMember = cWantedMembers;
+ while (iMember-- > 0)
+ {
+ if ( !strncmp(pszPrefix, paWantedMembers[iMember].pszName, cchPrefix)
+ && !strcmp(pszMember, paWantedMembers[iMember].pszName + cchPrefix))
+ {
+ paWantedMembers[iMember].off = offMember;
+ paWantedMembers[iMember].cb = cbMember;
+ cHits++;
+ }
+ else if (paWantedMembers[iMember].pszzAltNames)
+ {
+ char const *pszCur = paWantedMembers[iMember].pszzAltNames;
+ while (*pszCur)
+ {
+ size_t cchCur = strlen(pszCur);
+ if ( !strncmp(pszPrefix, pszCur, cchPrefix)
+ && !strcmp(pszMember, pszCur + cchPrefix))
+ {
+ paWantedMembers[iMember].off = offMember;
+ paWantedMembers[iMember].cb = cbMember;
+ cHits++;
+ break;
+ }
+ pszCur += cchCur + 1;
+ }
+ }
+ }
+ return cHits;
+}
+
+
+#if 0
+/**
+ * Resets the writable structure members prior to processing a PDB.
+ *
+ * While processing the PDB, will fill in the sizes and offsets of what we find.
+ * Afterwards we'll use look for reset values to see that every structure and
+ * member was located successfully.
+ */
+static void resetMyStructs(void)
+{
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_aStructs); i++)
+ {
+ PMYMEMBER paMembers = g_aStructs[i].paMembers;
+ uint32_t j = g_aStructs[i].cMembers;
+ while (j-- > 0)
+ {
+ paMembers[j].off = UINT32_MAX;
+ paMembers[j].cb = UINT32_MAX;
+ }
+ }
+}
+#endif
+
+
+/**
+ * Find members in the specified structure type (@a idxType).
+ *
+ * @returns Fully bitched exit code.
+ * @param hFake Fake process handle.
+ * @param uModAddr The module address.
+ * @param idxType The type index of the structure which members we're
+ * going to process.
+ * @param cWantedMembers The number of wanted members.
+ * @param paWantedMembers The wanted members. This will be modified.
+ * @param offDisp Displacement when calculating member offsets.
+ * @param pszStructNm The top level structure name.
+ * @param pszPrefix The member name prefix.
+ * @param pszLogTag The log tag.
+ */
+static RTEXITCODE findMembers(HANDLE hFake, uint64_t uModAddr, uint32_t idxType,
+ uint32_t cWantedMembers, PMYMEMBER paWantedMembers,
+ uint32_t offDisp, const char *pszStructNm, const char *pszPrefix, const char *pszLogTag)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+
+ DWORD cChildren = 0;
+ if (!SymGetTypeInfo(hFake, uModAddr, idxType, TI_GET_CHILDRENCOUNT, &cChildren))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: TI_GET_CHILDRENCOUNT failed on _KPRCB: %u\n", pszLogTag, GetLastError());
+
+ MyDbgPrintf(" %s: cChildren=%u (%#x)\n", pszStructNm, cChildren);
+ TI_FINDCHILDREN_PARAMS *pChildren;
+ pChildren = (TI_FINDCHILDREN_PARAMS *)alloca(RT_UOFFSETOF_DYN(TI_FINDCHILDREN_PARAMS, ChildId[cChildren]));
+ pChildren->Start = 0;
+ pChildren->Count = cChildren;
+ if (!SymGetTypeInfo(hFake, uModAddr, idxType, TI_FINDCHILDREN, pChildren))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: TI_FINDCHILDREN failed on _KPRCB: %u\n", pszLogTag, GetLastError());
+
+ for (uint32_t i = 0; i < cChildren; i++)
+ {
+ //MyDbgPrintf(" %s: child#%u: TypeIndex=%u\n", pszStructNm, i, pChildren->ChildId[i]);
+ IMAGEHLP_SYMBOL_TYPE_INFO enmErr;
+ PWCHAR pwszMember = NULL;
+ uint32_t idxRefType = 0;
+ uint32_t offMember = 0;
+ uint64_t cbMember = 0;
+ uint32_t cMemberChildren = 0;
+ if ( SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_SYMNAME, &pwszMember)
+ && SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_OFFSET, &offMember)
+ && SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], enmErr = TI_GET_TYPE, &idxRefType)
+ && SymGetTypeInfo(hFake, uModAddr, idxRefType, enmErr = TI_GET_LENGTH, &cbMember)
+ && SymGetTypeInfo(hFake, uModAddr, idxRefType, enmErr = TI_GET_CHILDRENCOUNT, &cMemberChildren)
+ )
+ {
+ offMember += offDisp;
+
+ char *pszMember;
+ int rc = RTUtf16ToUtf8(pwszMember, &pszMember);
+ if (RT_SUCCESS(rc))
+ {
+ matchUpStructMembers(cWantedMembers, paWantedMembers, pszPrefix, pszMember, offMember, cbMember);
+
+ /*
+ * Gather more info and do some debug printing. We'll use some
+ * of this info below when recursing into sub-structures
+ * and arrays.
+ */
+ uint32_t fNested = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_NESTED, &fNested);
+ uint32_t uDataKind = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_DATAKIND, &uDataKind);
+ uint32_t uBaseType = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_BASETYPE, &uBaseType);
+ uint32_t uMembTag = 0; SymGetTypeInfo(hFake, uModAddr, pChildren->ChildId[i], TI_GET_SYMTAG, &uMembTag);
+ uint32_t uBaseTag = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_SYMTAG, &uBaseTag);
+ uint32_t cElements = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_COUNT, &cElements);
+ uint32_t idxArrayType = 0; SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_ARRAYINDEXTYPEID, &idxArrayType);
+ MyDbgPrintf(" %#06x LB %#06llx %c%c %2d %2d %2d %2d %2d %4d %s::%s%s\n",
+ offMember, cbMember,
+ cMemberChildren > 0 ? 'c' : '-',
+ fNested != 0 ? 'n' : '-',
+ uDataKind,
+ uBaseType,
+ uMembTag,
+ uBaseTag,
+ cElements,
+ idxArrayType,
+ pszStructNm,
+ pszPrefix,
+ pszMember);
+
+ /*
+ * Recurse into children.
+ */
+ if (cMemberChildren > 0)
+ {
+ size_t cbNeeded = strlen(pszMember) + strlen(pszPrefix) + sizeof(".");
+ char *pszSubPrefix = (char *)RTMemTmpAlloc(cbNeeded);
+ if (pszSubPrefix)
+ {
+ strcat(strcat(strcpy(pszSubPrefix, pszPrefix), pszMember), ".");
+ RTEXITCODE rcExit2 = findMembers(hFake, uModAddr, idxRefType, cWantedMembers,
+ paWantedMembers, offMember,
+ pszStructNm,
+ pszSubPrefix,
+ pszLogTag);
+ if (rcExit2 != RTEXITCODE_SUCCESS)
+ rcExit = rcExit2;
+ RTMemTmpFree(pszSubPrefix);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory\n");
+ }
+ /*
+ * Recurse into arrays too.
+ */
+ else if (cElements > 0 && idxArrayType > 0)
+ {
+ BOOL fRc;
+ uint32_t idxElementRefType = 0;
+ fRc = SymGetTypeInfo(hFake, uModAddr, idxRefType, TI_GET_TYPE, &idxElementRefType); Assert(fRc);
+ uint64_t cbElement = cbMember / cElements;
+ fRc = SymGetTypeInfo(hFake, uModAddr, idxElementRefType, TI_GET_LENGTH, &cbElement); Assert(fRc);
+ MyDbgPrintf("idxArrayType=%u idxElementRefType=%u cbElement=%u\n", idxArrayType, idxElementRefType, cbElement);
+
+ size_t cbNeeded = strlen(pszMember) + strlen(pszPrefix) + sizeof("[xxxxxxxxxxxxxxxx].");
+ char *pszSubPrefix = (char *)RTMemTmpAlloc(cbNeeded);
+ if (pszSubPrefix)
+ {
+ for (uint32_t iElement = 0; iElement < cElements; iElement++)
+ {
+ RTStrPrintf(pszSubPrefix, cbNeeded, "%s%s[%u].", pszPrefix, pszMember, iElement);
+ RTEXITCODE rcExit2 = findMembers(hFake, uModAddr, idxElementRefType, cWantedMembers,
+ paWantedMembers,
+ offMember + iElement * cbElement,
+ pszStructNm,
+ pszSubPrefix,
+ pszLogTag);
+ if (rcExit2 != RTEXITCODE_SUCCESS)
+ rcExit = rcExit2;
+ }
+ RTMemTmpFree(pszSubPrefix);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory\n");
+ }
+
+ RTStrFree(pszMember);
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: RTUtf16ToUtf8 failed on %s child#%u: %Rrc\n",
+ pszLogTag, pszStructNm, i, rc);
+ }
+ /* TI_GET_OFFSET fails on bitfields, so just ignore+skip those. */
+ else if (enmErr != TI_GET_OFFSET || GetLastError() != ERROR_INVALID_FUNCTION)
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: SymGetTypeInfo(,,,%d,) failed on %s child#%u: %u\n",
+ pszLogTag, enmErr, pszStructNm, i, GetLastError());
+ LocalFree(pwszMember);
+ } /* For each child. */
+
+ return rcExit;
+}
+
+
+/**
+ * Lookup up structures and members in the given module.
+ *
+ * @returns Fully bitched exit code.
+ * @param hFake Fake process handle.
+ * @param uModAddr The module address.
+ * @param pszLogTag The log tag.
+ * @param pszPdb The full PDB path.
+ * @param pOsVerInfo The OS version info for altering the error handling
+ * for older OSes.
+ */
+static RTEXITCODE findStructures(HANDLE hFake, uint64_t uModAddr, const char *pszLogTag, const char *pszPdb,
+ PCRTNTSDBOSVER pOsVerInfo)
+{
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ PSYMBOL_INFO pSymInfo = (PSYMBOL_INFO)alloca(sizeof(*pSymInfo));
+ for (uint32_t iStruct = 0; iStruct < RT_ELEMENTS(g_aStructs); iStruct++)
+ {
+ pSymInfo->SizeOfStruct = sizeof(*pSymInfo);
+ pSymInfo->MaxNameLen = 0;
+ if (!SymGetTypeFromName(hFake, uModAddr, g_aStructs[iStruct].pszName, pSymInfo))
+ {
+ if (!(pOsVerInfo->uMajorVer == 5 && pOsVerInfo->uMinorVer == 0) /* w2k */)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s: Failed to find _KPRCB: %u\n", pszPdb, GetLastError());
+ RTMsgInfo("%s: Skipping - failed to find _KPRCB: %u\n", pszPdb, GetLastError());
+ return RTEXITCODE_SKIPPED;
+ }
+
+ MyDbgPrintf(" %s: TypeIndex=%u\n", g_aStructs[iStruct].pszName, pSymInfo->TypeIndex);
+ MyDbgPrintf(" %s: Size=%u (%#x)\n", g_aStructs[iStruct].pszName, pSymInfo->Size, pSymInfo->Size);
+
+ rcExit = findMembers(hFake, uModAddr, pSymInfo->TypeIndex,
+ g_aStructs[iStruct].cMembers, g_aStructs[iStruct].paMembers, 0 /* offDisp */,
+ g_aStructs[iStruct].pszName, "", pszLogTag);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return rcExit;
+ } /* for each struct we want */
+ return rcExit;
+}
+
+
+#if 0 /* unused */
+static bool strIEndsWith(const char *pszString, const char *pszSuffix)
+{
+ size_t cchString = strlen(pszString);
+ size_t cchSuffix = strlen(pszSuffix);
+ if (cchString < cchSuffix)
+ return false;
+ return RTStrICmp(pszString + cchString - cchSuffix, pszSuffix) == 0;
+}
+#endif
+
+
+/**
+ * Use various hysterics to figure out the OS version details from the PDB path.
+ *
+ * This ASSUMES quite a bunch of things:
+ * -# Working on unpacked symbol packages. This does not work for
+ * windbg symbol stores/caches.
+ * -# The symbol package has been unpacked into a directory with the same
+ * name as the symbol package (sans suffixes).
+ *
+ * @returns Fully complained exit code.
+ * @param pszPdb The path to the PDB.
+ * @param pVerInfo Where to return the version info.
+ * @param penmArch Where to return the architecture.
+ */
+static RTEXITCODE FigurePdbVersionInfo(const char *pszPdb, PRTNTSDBOSVER pVerInfo, MYARCH *penmArch)
+{
+ /*
+ * Split the path.
+ */
+ union
+ {
+ RTPATHSPLIT Split;
+ uint8_t abPad[RTPATH_MAX + 1024];
+ } u;
+ int rc = RTPathSplit(pszPdb, &u.Split, sizeof(u), 0);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathSplit failed on '%s': %Rrc", pszPdb, rc);
+ if (!(u.Split.fProps & RTPATH_PROP_FILENAME))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPATH_PROP_FILENAME not set for: '%s'", pszPdb);
+ const char *pszFilename = u.Split.apszComps[u.Split.cComps - 1];
+
+ /*
+ * SMP or UNI kernel?
+ */
+ if ( !RTStrICmp(pszFilename, "ntkrnlmp.pdb")
+ || !RTStrICmp(pszFilename, "ntkrpamp.pdb")
+ )
+ pVerInfo->fSmp = true;
+ else if ( !RTStrICmp(pszFilename, "ntoskrnl.pdb")
+ || !RTStrICmp(pszFilename, "ntkrnlpa.pdb")
+ )
+ pVerInfo->fSmp = false;
+ else
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Doesn't recognize the filename '%s'...", pszFilename);
+
+ /*
+ * Look for symbol pack names in the path. This is stuff like:
+ * - WindowsVista.6002.090410-1830.x86fre
+ * - WindowsVista.6002.090410-1830.amd64chk
+ * - Windows_Win7.7600.16385.090713-1255.X64CHK
+ * - Windows_Win7SP1.7601.17514.101119-1850.AMD64FRE
+ * - Windows_Win8.9200.16384.120725-1247.X86CHK
+ * - en_windows_8_1_symbols_debug_checked_x64_2712568
+ */
+ uint32_t i = u.Split.cComps - 1;
+ while (i-- > 0)
+ {
+ static struct
+ {
+ const char *pszPrefix;
+ size_t cchPrefix;
+ uint8_t uMajorVer;
+ uint8_t uMinorVer;
+ uint8_t uCsdNo;
+ uint32_t uBuildNo; /**< UINT32_MAX means the number immediately after the prefix. */
+ } const s_aSymPacks[] =
+ {
+ { RT_STR_TUPLE("w2kSP1SYM"), 5, 0, 1, 2195 },
+ { RT_STR_TUPLE("w2ksp2srp1"), 5, 0, 2, 2195 },
+ { RT_STR_TUPLE("w2ksp2sym"), 5, 0, 2, 2195 },
+ { RT_STR_TUPLE("w2ksp3sym"), 5, 0, 3, 2195 },
+ { RT_STR_TUPLE("w2ksp4sym"), 5, 0, 4, 2195 },
+ { RT_STR_TUPLE("Windows2000-KB891861"), 5, 0, 4, 2195 },
+ { RT_STR_TUPLE("windowsxp"), 5, 1, 0, 2600 },
+ { RT_STR_TUPLE("xpsp1sym"), 5, 1, 1, 2600 },
+ { RT_STR_TUPLE("WindowsXP-KB835935-SP2-"), 5, 1, 2, 2600 },
+ { RT_STR_TUPLE("WindowsXP-KB936929-SP3-"), 5, 1, 3, 2600 },
+ { RT_STR_TUPLE("Windows2003."), 5, 2, 0, 3790 },
+ { RT_STR_TUPLE("Windows2003_sp1."), 5, 2, 1, 3790 },
+ { RT_STR_TUPLE("WindowsServer2003-KB933548-v1"), 5, 2, 1, 3790 },
+ { RT_STR_TUPLE("WindowsVista.6000."), 6, 0, 0, 6000 },
+ { RT_STR_TUPLE("Windows_Longhorn.6001."), 6, 0, 1, 6001 }, /* incl w2k8 */
+ { RT_STR_TUPLE("WindowsVista.6002."), 6, 0, 2, 6002 }, /* incl w2k8 */
+ { RT_STR_TUPLE("Windows_Winmain.7000"), 6, 1, 0, 7000 }, /* Beta */
+ { RT_STR_TUPLE("Windows_Winmain.7100"), 6, 1, 0, 7100 }, /* RC */
+ { RT_STR_TUPLE("Windows_Win7.7600"), 6, 1, 0, 7600 }, /* RC */
+ { RT_STR_TUPLE("Windows_Win7SP1.7601"), 6, 1, 1, 7601 }, /* RC */
+ { RT_STR_TUPLE("Windows_Winmain.8102"), 6, 2, 0, 8102 }, /* preview */
+ { RT_STR_TUPLE("Windows_Winmain.8250"), 6, 2, 0, 8250 }, /* beta */
+ { RT_STR_TUPLE("Windows_Winmain.8400"), 6, 2, 0, 8400 }, /* RC */
+ { RT_STR_TUPLE("Windows_Win8.9200"), 6, 2, 0, 9200 }, /* RTM */
+ { RT_STR_TUPLE("en_windows_8_1"), 6, 3, 0, 9600 }, /* RTM */
+ { RT_STR_TUPLE("en_windows_10_symbols_"), 10, 0, 0,10240 }, /* RTM */
+ { RT_STR_TUPLE("en_windows_10_symbols_"), 10, 0, 0,10240 }, /* RTM */
+ { RT_STR_TUPLE("en_windows_10_17134_"), 10, 0, 0,17134 }, /* 1803 */
+ };
+
+ const char *pszComp = u.Split.apszComps[i];
+ uint32_t iSymPack = RT_ELEMENTS(s_aSymPacks);
+ while (iSymPack-- > 0)
+ if (!RTStrNICmp(pszComp, s_aSymPacks[iSymPack].pszPrefix, s_aSymPacks[iSymPack].cchPrefix))
+ break;
+ if (iSymPack >= RT_ELEMENTS(s_aSymPacks))
+ continue;
+
+ pVerInfo->uMajorVer = s_aSymPacks[iSymPack].uMajorVer;
+ pVerInfo->uMinorVer = s_aSymPacks[iSymPack].uMinorVer;
+ pVerInfo->uCsdNo = s_aSymPacks[iSymPack].uCsdNo;
+ pVerInfo->fChecked = false;
+ pVerInfo->uBuildNo = s_aSymPacks[iSymPack].uBuildNo;
+
+ /* Parse build number if necessary. */
+ if (s_aSymPacks[iSymPack].uBuildNo == UINT32_MAX)
+ {
+ char *pszNext;
+ rc = RTStrToUInt32Ex(pszComp + s_aSymPacks[iSymPack].cchPrefix, &pszNext, 10, &pVerInfo->uBuildNo);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to decode build number in '%s': %Rrc", pszComp, rc);
+ if (*pszNext != '.' && *pszNext != '_' && *pszNext != '-')
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to decode build number in '%s': '%c'", pszComp, *pszNext);
+ }
+
+ /* Look for build arch and checked/free. */
+ if ( RTStrIStr(pszComp, ".x86.chk.")
+ || RTStrIStr(pszComp, ".x86chk.")
+ || RTStrIStr(pszComp, "_x86_chk_")
+ || RTStrIStr(pszComp, "_x86chk_")
+ || RTStrIStr(pszComp, "-x86-DEBUG")
+ || (RTStrIStr(pszComp, "-x86-") && RTStrIStr(pszComp, "-DEBUG"))
+ || RTStrIStr(pszComp, "_debug_checked_x86")
+ )
+ {
+ pVerInfo->fChecked = true;
+ *penmArch = MYARCH_X86;
+ }
+ else if ( RTStrIStr(pszComp, ".amd64.chk.")
+ || RTStrIStr(pszComp, ".amd64chk.")
+ || RTStrIStr(pszComp, ".x64.chk.")
+ || RTStrIStr(pszComp, ".x64chk.")
+ || RTStrIStr(pszComp, "_debug_checked_x64")
+ )
+ {
+ pVerInfo->fChecked = true;
+ *penmArch = MYARCH_AMD64;
+ }
+ else if ( RTStrIStr(pszComp, ".amd64.fre.")
+ || RTStrIStr(pszComp, ".amd64fre.")
+ || RTStrIStr(pszComp, ".x64.fre.")
+ || RTStrIStr(pszComp, ".x64fre.")
+ )
+ {
+ pVerInfo->fChecked = false;
+ *penmArch = MYARCH_AMD64;
+ }
+ else if ( RTStrIStr(pszComp, "DEBUG")
+ || RTStrIStr(pszComp, "_chk")
+ )
+ {
+ pVerInfo->fChecked = true;
+ *penmArch = MYARCH_X86;
+ }
+ else if (RTStrIStr(pszComp, "_x64"))
+ {
+ pVerInfo->fChecked = false;
+ *penmArch = MYARCH_AMD64;
+ }
+ else
+ {
+ pVerInfo->fChecked = false;
+ *penmArch = MYARCH_X86;
+ }
+ return RTEXITCODE_SUCCESS;
+ }
+
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Giving up on '%s'...\n", pszPdb);
+}
+
+
+/**
+ * Process one PDB.
+ *
+ * @returns Fully bitched exit code.
+ * @param pszPdb The path to the PDB.
+ */
+static RTEXITCODE processPdb(const char *pszPdb)
+{
+ /*
+ * We need the size later on, so get that now and present proper IPRT error
+ * info if the file is missing or inaccessible.
+ */
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszPdb, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathQueryInfo fail on '%s': %Rrc\n", pszPdb, rc);
+
+ /*
+ * Figure the windows version details for the given PDB.
+ */
+ MYARCH enmArch;
+ RTNTSDBOSVER OsVerInfo;
+ RTEXITCODE rcExit = FigurePdbVersionInfo(pszPdb, &OsVerInfo, &enmArch);
+ if (rcExit != RTEXITCODE_SUCCESS)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to figure the OS version info for '%s'.\n'", pszPdb);
+
+ /*
+ * Create a fake handle and open the PDB.
+ */
+ static uintptr_t s_iHandle = 0;
+ HANDLE hFake = (HANDLE)++s_iHandle;
+ if (!SymInitialize(hFake, NULL, FALSE))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "SymInitialied failed: %u\n", GetLastError());
+
+ uint64_t uModAddr = UINT64_C(0x1000000);
+ uModAddr = SymLoadModuleEx(hFake, NULL /*hFile*/, pszPdb, NULL /*pszModuleName*/,
+ uModAddr, ObjInfo.cbObject, NULL /*pData*/, 0 /*fFlags*/);
+ if (uModAddr != 0)
+ {
+ MyDbgPrintf("*** uModAddr=%#llx \"%s\" ***\n", uModAddr, pszPdb);
+
+ char szLogTag[32];
+ RTStrCopy(szLogTag, sizeof(szLogTag), RTPathFilename(pszPdb));
+
+ /*
+ * Find the structures.
+ */
+ rcExit = findStructures(hFake, uModAddr, szLogTag, pszPdb, &OsVerInfo);
+ if (rcExit == RTEXITCODE_SUCCESS)
+ rcExit = checkThatWeFoundEverything();
+ if (rcExit == RTEXITCODE_SUCCESS)
+ {
+ /*
+ * Save the details for later when we produce the header.
+ */
+ rcExit = saveStructures(&OsVerInfo, enmArch, pszPdb);
+ }
+ }
+ else
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "SymLoadModuleEx failed: %u\n", GetLastError());
+
+ if (!SymCleanup(hFake))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "SymCleanup failed: %u\n", GetLastError());
+
+ if (rcExit == RTEXITCODE_SKIPPED)
+ rcExit = RTEXITCODE_SUCCESS;
+ return rcExit;
+}
+
+
+/** The size of the directory entry buffer we're using. */
+#define MY_DIRENTRY_BUF_SIZE (sizeof(RTDIRENTRYEX) + RTPATH_MAX)
+
+/**
+ * Checks if the name is of interest to us.
+ *
+ * @returns true/false.
+ * @param pszName The name.
+ * @param cchName The length of the name.
+ */
+static bool isInterestingName(const char *pszName, size_t cchName)
+{
+ static struct { const char *psz; size_t cch; } const s_aNames[] =
+ {
+ { RT_STR_TUPLE("ntoskrnl.pdb") },
+ { RT_STR_TUPLE("ntkrnlmp.pdb") },
+ { RT_STR_TUPLE("ntkrnlpa.pdb") },
+ { RT_STR_TUPLE("ntkrpamp.pdb") },
+ };
+
+ if ( cchName == s_aNames[0].cch
+ && (pszName[0] == 'n' || pszName[0] == 'N')
+ && (pszName[1] == 't' || pszName[1] == 'T')
+ )
+ {
+ int i = RT_ELEMENTS(s_aNames);
+ while (i-- > 0)
+ if ( s_aNames[i].cch == cchName
+ && !RTStrICmp(s_aNames[i].psz, pszName))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Recursively processes relevant files in the specified directory.
+ *
+ * @returns Fully complained exit code.
+ * @param pszDir Pointer to the directory buffer.
+ * @param cchDir The length of pszDir in pszDir.
+ * @param pDirEntry Pointer to the directory buffer.
+ * @param iLogDepth The logging depth.
+ */
+static RTEXITCODE processDirSub(char *pszDir, size_t cchDir, PRTDIRENTRYEX pDirEntry, int iLogDepth)
+{
+ Assert(cchDir > 0); Assert(pszDir[cchDir] == '\0');
+
+ /* Make sure we've got some room in the path, to save us extra work further down. */
+ if (cchDir + 3 >= RTPATH_MAX)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s'\n", pszDir);
+
+ /* Open directory. */
+ RTDIR hDir;
+ int rc = RTDirOpen(&hDir, pszDir);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirOpen failed on '%s': %Rrc\n", pszDir, rc);
+
+ /* Ensure we've got a trailing slash (there is space for it see above). */
+ if (!RTPATH_IS_SEP(pszDir[cchDir - 1]))
+ {
+ pszDir[cchDir++] = RTPATH_SLASH;
+ pszDir[cchDir] = '\0';
+ }
+
+ /*
+ * Process the files and subdirs.
+ */
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ for (;;)
+ {
+ /* Get the next directory. */
+ size_t cbDirEntry = MY_DIRENTRY_BUF_SIZE;
+ rc = RTDirReadEx(hDir, pDirEntry, &cbDirEntry, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK);
+ if (RT_FAILURE(rc))
+ break;
+
+ /* Skip the dot and dot-dot links. */
+ if (RTDirEntryExIsStdDotLink(pDirEntry))
+ continue;
+
+ /* Check length. */
+ if (pDirEntry->cbName + cchDir + 3 >= RTPATH_MAX)
+ {
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Path too long: '%s' in '%.*s'\n", pDirEntry->szName, cchDir, pszDir);
+ break;
+ }
+
+ if (RTFS_IS_FILE(pDirEntry->Info.Attr.fMode))
+ {
+ /*
+ * Process debug info files of interest.
+ */
+ if (isInterestingName(pDirEntry->szName, pDirEntry->cbName))
+ {
+ memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
+ RTEXITCODE rcExit2 = processPdb(pszDir);
+ if (rcExit2 != RTEXITCODE_SUCCESS)
+ rcExit = rcExit2;
+ }
+ }
+ else if (RTFS_IS_DIRECTORY(pDirEntry->Info.Attr.fMode))
+ {
+ /*
+ * Recurse into the subdirectory. In order to speed up Win7+
+ * symbol pack traversals, we skip directories with ".pdb" suffixes
+ * unless they match any of the .pdb files we're looking for.
+ *
+ * Note! When we get back pDirEntry will be invalid.
+ */
+ if ( pDirEntry->cbName <= 4
+ || RTStrICmp(&pDirEntry->szName[pDirEntry->cbName - 4], ".pdb")
+ || isInterestingName(pDirEntry->szName, pDirEntry->cbName))
+ {
+ memcpy(&pszDir[cchDir], pDirEntry->szName, pDirEntry->cbName + 1);
+ if (iLogDepth > 0)
+ RTMsgInfo("%s%s ...\n", pszDir, RTPATH_SLASH_STR);
+ RTEXITCODE rcExit2 = processDirSub(pszDir, cchDir + pDirEntry->cbName, pDirEntry, iLogDepth - 1);
+ if (rcExit2 != RTEXITCODE_SUCCESS)
+ rcExit = rcExit2;
+ }
+ }
+ }
+ if (rc != VERR_NO_MORE_FILES)
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirReadEx failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
+
+ rc = RTDirClose(hDir);
+ if (RT_FAILURE(rc))
+ rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirClose failed: %Rrc\npszDir=%.*s", rc, cchDir, pszDir);
+ return rcExit;
+}
+
+
+/**
+ * Recursively processes relevant files in the specified directory.
+ *
+ * @returns Fully complained exit code.
+ * @param pszDir The directory to search.
+ */
+static RTEXITCODE processDir(const char *pszDir)
+{
+ char szPath[RTPATH_MAX];
+ int rc = RTPathAbs(pszDir, szPath, sizeof(szPath));
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed on '%s': %Rrc\n", pszDir, rc);
+
+ union
+ {
+ uint8_t abPadding[MY_DIRENTRY_BUF_SIZE];
+ RTDIRENTRYEX DirEntry;
+ } uBuf;
+ return processDirSub(szPath, strlen(szPath), &uBuf.DirEntry, g_iOptVerbose);
+}
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTR3InitExe(argc, &argv, 0 /*fFlags*/);
+ if (RT_FAILURE(rc))
+ return RTMsgInitFailure(rc);
+
+ RTListInit(&g_SetList);
+
+ /*
+ * Parse options.
+ */
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--force", 'f', RTGETOPT_REQ_NOTHING },
+ { "--output", 'o', RTGETOPT_REQ_STRING },
+ { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+ { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
+ };
+
+ RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
+ const char *pszOutput = "-";
+
+ int ch;
+ RTGETOPTUNION ValueUnion;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
+ RTGETOPTINIT_FLAGS_OPTS_FIRST);
+ while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
+ {
+ switch (ch)
+ {
+ case 'f':
+ g_fOptForce = true;
+ break;
+
+ case 'v':
+ g_iOptVerbose++;
+ break;
+
+ case 'q':
+ g_iOptVerbose++;
+ break;
+
+ case 'o':
+ pszOutput = ValueUnion.psz;
+ break;
+
+ case 'V':
+ RTPrintf("$Revision: 155249 $");
+ break;
+
+ case 'h':
+ RTPrintf("usage: %s [-v|--verbose] [-q|--quiet] [-f|--force] [-o|--output <file.h>] <dir1|pdb1> [...]\n"
+ " or: %s [-V|--version]\n"
+ " or: %s [-h|--help]\n",
+ argv[0], argv[0], argv[0]);
+ return RTEXITCODE_SUCCESS;
+
+ case VINF_GETOPT_NOT_OPTION:
+ {
+ RTEXITCODE rcExit2;
+ if (RTFileExists(ValueUnion.psz))
+ rcExit2 = processPdb(ValueUnion.psz);
+ else
+ rcExit2 = processDir(ValueUnion.psz);
+ if (rcExit2 != RTEXITCODE_SUCCESS)
+ {
+ if (!g_fOptForce)
+ return rcExit2;
+ rcExit = rcExit2;
+ }
+ break;
+ }
+
+ default:
+ return RTGetOptPrintError(ch, &ValueUnion);
+ }
+ }
+ if (RTListIsEmpty(&g_SetList))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "No usable debug files found.\n");
+
+ /*
+ * Generate the output.
+ */
+ PRTSTREAM pOut = g_pStdOut;
+ if (strcmp(pszOutput, "-"))
+ {
+ rc = RTStrmOpen(pszOutput, "w", &pOut);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s' for writing: %Rrc\n", pszOutput, rc);
+ }
+
+ generateHeader(pOut);
+
+ if (pOut != g_pStdOut)
+ rc = RTStrmClose(pOut);
+ else
+ rc = RTStrmFlush(pOut);
+ if (RT_FAILURE(rc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error %s '%s': %Rrc\n", pszOutput,
+ pOut != g_pStdOut ? "closing" : "flushing", rc);
+ return rcExit;
+}
diff --git a/src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp
new file mode 100644
index 00000000..b3de7f03
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/process-r0drv-nt.cpp
@@ -0,0 +1,55 @@
+/* $Id: process-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Process, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+#include <iprt/process.h>
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ return (RTPROCESS)(uintptr_t)PsGetCurrentProcessId();
+}
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ return (RTR0PROCESS)PsGetCurrentProcess();
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp
new file mode 100644
index 00000000..0758f1e1
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/semfastmutex-r0drv-nt.cpp
@@ -0,0 +1,148 @@
+/* $Id: semfastmutex-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+#include <iprt/semaphore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/errcore.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the linux semaphore structure.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** the NT fast mutex. */
+ FAST_MUTEX Mutex;
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ /*
+ * Allocate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis;
+ Assert(sizeof(*pThis) > sizeof(void *));
+ pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize.
+ */
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ ExInitializeFastMutex(&pThis->Mutex);
+
+ *phFastMtx = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ /*
+ * Validate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ Assert(pThis->Mutex.Count == 1);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ /*
+ * Validate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+#if 1
+ /*
+ * ExAcquireFastMutex will set the IRQL to APC regardless of our current
+ * level. Lowering the IRQL may screw things up, so do not allow this.
+ */
+# if 0 /** @todo enable this when the logger has been fixed. */
+ AssertMsg(KeGetCurrentIrql() <= APC_LEVEL, ("%d\n", KeGetCurrentIrql()), VERR_INVALID_STATE);
+# else /* the gentler approach. */
+ KIRQL Irql = KeGetCurrentIrql();
+ if (Irql > APC_LEVEL)
+ return VERR_INVALID_STATE;
+# endif
+#endif
+
+ ExAcquireFastMutex(&pThis->Mutex);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ /*
+ * Validate.
+ */
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ ExReleaseFastMutex(&pThis->Mutex);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp
new file mode 100644
index 00000000..30e8c408
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/semmutex-r0drv-nt.cpp
@@ -0,0 +1,246 @@
+/* $Id: semmutex-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Mutex Semaphores, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 RTSEMMUTEX_WITHOUT_REMAPPING
+#include "the-nt-kernel.h"
+#include <iprt/semaphore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * NT mutex semaphore.
+ */
+typedef struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t volatile u32Magic;
+#ifdef RT_USE_FAST_MUTEX
+ /** The fast mutex object. */
+ FAST_MUTEX Mutex;
+#else
+ /** The NT Mutex object. */
+ KMUTEX Mutex;
+#endif
+} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL;
+
+
+
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ RT_NOREF3(hClass, uSubClass, pszNameFmt);
+
+ AssertCompile(sizeof(RTSEMMUTEXINTERNAL) > sizeof(void *));
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+#ifdef RT_USE_FAST_MUTEX
+ ExInitializeFastMutex(&pThis->Mutex);
+#else
+ KeInitializeMutex(&pThis->Mutex, 0);
+#endif
+
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem;
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate it and signal the object just in case.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Internal worker for RTSemMutexRequest and RTSemMutexRequestNoResume
+ *
+ * @returns IPRT status code.
+ * @param hMutexSem The mutex handle.
+ * @param cMillies The timeout.
+ * @param fInterruptible Whether it's interruptible
+ * (RTSemMutexRequestNoResume) or not
+ * (RTSemMutexRequest).
+ */
+static int rtSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, BOOLEAN fInterruptible)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Get the mutex.
+ */
+#ifdef RT_USE_FAST_MUTEX
+ AssertMsg(cMillies == RT_INDEFINITE_WAIT, ("timeouts are not supported when using fast mutexes!\n"));
+ ExAcquireFastMutex(&pThis->Mutex);
+ return VINF_SUCCESS;
+
+#else /* !RT_USE_FAST_MUTEX */
+ NTSTATUS rcNt;
+ if (cMillies == RT_INDEFINITE_WAIT)
+ rcNt = KeWaitForSingleObject(&pThis->Mutex, Executive, KernelMode, fInterruptible, NULL);
+ else
+ {
+ LARGE_INTEGER Timeout;
+ Timeout.QuadPart = -(int64_t)cMillies * 10000;
+ rcNt = KeWaitForSingleObject(&pThis->Mutex, Executive, KernelMode, fInterruptible, &Timeout);
+ }
+ switch (rcNt)
+ {
+ case STATUS_SUCCESS:
+ if (pThis->u32Magic == RTSEMMUTEX_MAGIC)
+ return VINF_SUCCESS;
+ return VERR_SEM_DESTROYED;
+
+ case STATUS_ALERTED:
+ case STATUS_USER_APC:
+ Assert(fInterruptible);
+ return VERR_INTERRUPTED;
+
+ case STATUS_TIMEOUT:
+ return VERR_TIMEOUT;
+
+ default:
+ AssertMsgFailed(("pThis->u32Magic=%RX32 pThis=%p: wait returned %lx!\n",
+ pThis->u32Magic, pThis, (long)rcNt));
+ return VERR_INTERNAL_ERROR;
+ }
+#endif /* !RT_USE_FAST_MUTEX */
+}
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexRequest(hMutexSem, cMillies, FALSE /*fInterruptible*/);
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RT_NOREF1(uId); RT_SRC_POS_NOREF();
+ return RTSemMutexRequest(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexRequest(hMutexSem, cMillies, TRUE /*fInterruptible*/);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RT_NOREF1(uId); RT_SRC_POS_NOREF();
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Release the mutex.
+ */
+#ifdef RT_USE_FAST_MUTEX
+ ExReleaseFastMutex(&pThis->Mutex);
+#else
+ KeReleaseMutex(&pThis->Mutex, FALSE /*Wait*/);
+#endif
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
+
+#ifdef RT_USE_FAST_MUTEX
+ return pThis->Mutex && pThis->Mutex->Owner != NULL;
+#else
+ return KeReadStateMutex(&pThis->Mutex) == 0;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp
new file mode 100644
index 00000000..2e7b1455
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/spinlock-r0drv-nt.cpp
@@ -0,0 +1,207 @@
+/* $Id: spinlock-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+
+#include <iprt/spinlock.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Apply the NoIrq hack if defined. */
+#define RTSPINLOCK_NT_HACK_NOIRQ
+
+#ifdef RTSPINLOCK_NT_HACK_NOIRQ
+/** Indicates that the spinlock is taken. */
+# define RTSPINLOCK_NT_HACK_NOIRQ_TAKEN UINT32(0x00c0ffee)
+/** Indicates that the spinlock is taken. */
+# define RTSPINLOCK_NT_HACK_NOIRQ_FREE UINT32(0xfe0000fe)
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the KSPIN_LOCK type.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+#ifdef RTSPINLOCK_NT_HACK_NOIRQ
+ /** Spinlock hack. */
+ uint32_t volatile u32Hack;
+#endif
+ /** The saved IRQL. */
+ KIRQL volatile SavedIrql;
+ /** The saved interrupt flag. */
+ RTCCUINTREG volatile fIntSaved;
+ /** The spinlock creation flags. */
+ uint32_t fFlags;
+ /** The NT spinlock structure. */
+ KSPIN_LOCK Spinlock;
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+ RT_NOREF1(pszName);
+
+ /*
+ * Allocate.
+ */
+ Assert(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize & return.
+ */
+ pThis->u32Magic = RTSPINLOCK_MAGIC;
+#ifdef RTSPINLOCK_NT_HACK_NOIRQ
+ pThis->u32Hack = RTSPINLOCK_NT_HACK_NOIRQ_FREE;
+#endif
+ pThis->SavedIrql = 0;
+ pThis->fIntSaved = 0;
+ pThis->fFlags = fFlags;
+ KeInitializeSpinLock(&pThis->Spinlock);
+
+ *pSpinlock = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ if (pThis->u32Magic != RTSPINLOCK_MAGIC)
+ {
+ AssertMsgFailed(("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ ASMAtomicIncU32(&pThis->u32Magic);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_MAGIC, ("magic=%#x\n", pThis->u32Magic));
+
+ KIRQL SavedIrql;
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+#ifndef RTSPINLOCK_NT_HACK_NOIRQ
+ RTCCUINTREG fIntSaved = ASMGetFlags();
+ ASMIntDisable();
+ KeAcquireSpinLock(&pThis->Spinlock, &SavedIrql);
+#else
+ SavedIrql = KeGetCurrentIrql();
+ if (SavedIrql < DISPATCH_LEVEL)
+ {
+ KeRaiseIrql(DISPATCH_LEVEL, &SavedIrql);
+ Assert(SavedIrql < DISPATCH_LEVEL);
+ }
+ RTCCUINTREG fIntSaved = ASMGetFlags();
+ ASMIntDisable();
+
+ if (!ASMAtomicCmpXchgU32(&pThis->u32Hack, RTSPINLOCK_NT_HACK_NOIRQ_TAKEN, RTSPINLOCK_NT_HACK_NOIRQ_FREE))
+ {
+ while (!ASMAtomicCmpXchgU32(&pThis->u32Hack, RTSPINLOCK_NT_HACK_NOIRQ_TAKEN, RTSPINLOCK_NT_HACK_NOIRQ_FREE))
+ ASMNopPause();
+ }
+#endif
+ pThis->fIntSaved = fIntSaved;
+ }
+ else
+ KeAcquireSpinLock(&pThis->Spinlock, &SavedIrql);
+ pThis->SavedIrql = SavedIrql;
+}
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertMsg(pThis && pThis->u32Magic == RTSPINLOCK_MAGIC, ("magic=%#x\n", pThis->u32Magic));
+
+ KIRQL SavedIrql = pThis->SavedIrql;
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+ RTCCUINTREG fIntSaved = pThis->fIntSaved;
+ pThis->fIntSaved = 0;
+
+#ifndef RTSPINLOCK_NT_HACK_NOIRQ
+ KeReleaseSpinLock(&pThis->Spinlock, SavedIrql);
+ ASMSetFlags(fIntSaved);
+#else
+ Assert(pThis->u32Hack == RTSPINLOCK_NT_HACK_NOIRQ_TAKEN);
+
+ ASMAtomicWriteU32(&pThis->u32Hack, RTSPINLOCK_NT_HACK_NOIRQ_FREE);
+ ASMSetFlags(fIntSaved);
+ if (SavedIrql < DISPATCH_LEVEL)
+ KeLowerIrql(SavedIrql);
+#endif
+ }
+ else
+ KeReleaseSpinLock(&pThis->Spinlock, SavedIrql);
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/symdb.h b/src/VBox/Runtime/r0drv/nt/symdb.h
new file mode 100644
index 00000000..931a8797
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/symdb.h
@@ -0,0 +1,98 @@
+/* $Id: symdb.h $ */
+/** @file
+ * IPRT - Internal Header for the NT Ring-0 Driver Symbol DB.
+ */
+
+/*
+ * Copyright (C) 2013-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 IPRT_INCLUDED_SRC_r0drv_nt_symdb_h
+#define IPRT_INCLUDED_SRC_r0drv_nt_symdb_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+
+/**
+ * NT Version info.
+ */
+typedef struct RTNTSDBOSVER
+{
+ /** The major version number. */
+ uint8_t uMajorVer;
+ /** The minor version number. */
+ uint8_t uMinorVer;
+ /** Set if checked build, clear if free (retail) build. */
+ uint8_t fChecked : 1;
+ /** Set if multi processor kernel. */
+ uint8_t fSmp : 1;
+ /** The service pack number. */
+ uint8_t uCsdNo : 6;
+ /** The build number. */
+ uint32_t uBuildNo;
+} RTNTSDBOSVER;
+/** Pointer to NT version info. */
+typedef RTNTSDBOSVER *PRTNTSDBOSVER;
+/** Pointer to const NT version info. */
+typedef RTNTSDBOSVER const *PCRTNTSDBOSVER;
+
+
+/**
+ * Compare NT OS version structures.
+ *
+ * @retval 0 if equal
+ * @retval 1 if @a pInfo1 is newer/greater than @a pInfo2
+ * @retval -1 if @a pInfo1 is older/less than @a pInfo2
+ *
+ * @param pInfo1 The first version info structure.
+ * @param pInfo2 The second version info structure.
+ */
+DECLINLINE(int) rtNtOsVerInfoCompare(PCRTNTSDBOSVER pInfo1, PCRTNTSDBOSVER pInfo2)
+{
+ if (pInfo1->uMajorVer != pInfo2->uMajorVer)
+ return pInfo1->uMajorVer > pInfo2->uMajorVer ? 1 : -1;
+ if (pInfo1->uMinorVer != pInfo2->uMinorVer)
+ return pInfo1->uMinorVer > pInfo2->uMinorVer ? 1 : -1;
+ if (pInfo1->uBuildNo != pInfo2->uBuildNo)
+ return pInfo1->uBuildNo > pInfo2->uBuildNo ? 1 : -1;
+ if (pInfo1->uCsdNo != pInfo2->uCsdNo)
+ return pInfo1->uCsdNo > pInfo2->uCsdNo ? 1 : -1;
+ if (pInfo1->fSmp != pInfo2->fSmp)
+ return pInfo1->fSmp > pInfo2->fSmp ? 1 : -1;
+ if (pInfo1->fChecked != pInfo2->fChecked)
+ return pInfo1->fChecked > pInfo2->fChecked ? 1 : -1;
+ return 0;
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_symdb_h */
+
diff --git a/src/VBox/Runtime/r0drv/nt/symdbdata.h b/src/VBox/Runtime/r0drv/nt/symdbdata.h
new file mode 100644
index 00000000..3bb8961a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/symdbdata.h
@@ -0,0 +1,2998 @@
+/* $Id: symdbdata.h $ */
+/** @file
+ * IPRT - NT kernel type helpers - Autogenerated, do NOT edit.
+ */
+
+/*
+ * Copyright (C) 2013-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 IPRT_INCLUDED_SRC_r0drv_nt_symdbdata_h
+#define IPRT_INCLUDED_SRC_r0drv_nt_symdbdata_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "r0drv/nt/symdb.h"
+
+typedef struct RTNTSDBTYPE_KPRCB
+{
+ uint32_t offQuantumEnd;
+ uint32_t cbQuantumEnd;
+ uint32_t offDpcQueueDepth;
+ uint32_t cbDpcQueueDepth;
+ uint32_t offVendorString;
+ uint32_t cbVendorString;
+} RTNTSDBTYPE_KPRCB;
+
+
+typedef struct RTNTSDBSET
+{
+ RTNTSDBOSVER OsVerInfo;
+ RTNTSDBTYPE_KPRCB KPRCB;
+} RTNTSDBSET;
+typedef RTNTSDBSET const *PCRTNTSDBSET;
+
+
+#ifndef RTNTSDB_NO_DATA
+const RTNTSDBSET g_artNtSdbSets[] =
+{
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_nec98\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_en\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_nec98\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_nec98\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_en\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_en\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp3sym_en_chk\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_en\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_nec98\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-x86-Symbols-ENU\symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_en\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_en\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_nec98\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_nec98\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\Windows2000-KB891861-nec98-Symbols-JPN\symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\uold\w2ksp4sym_en_chk\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 4,
+ /* .uBuildNo = */ 2195,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0750,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x06e8,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x072d,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.fre.rtm.symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\windowsxp.x86.chk.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\xpsp1sym_x86_chk\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-slp-Symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB835935-SP2-Debug-slp-Symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-symbols-full-ENU\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsXP-KB936929-SP3-x86-DEBUG-symbols-full-ENU-DEBUG\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 3,
+ /* .uBuildNo = */ 2600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x088c,
+ /* .cbQuantumEnd = */ 0x0004,
+ /* .offDpcQueueDepth = */ 0x0870,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0900,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.fre.rtm.symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003.x86.chk.rtm.symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x08c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x086c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0a78,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.fre.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU-DEBUG\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntkrnlpa.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.chk.rtm.symbols\exe\ntoskrnl.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ false,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.fre.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.fre.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x86-symbols-NRL-ENU-DEBUG\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsServer2003-KB933548-v1-x64-symbols-NRL-ENU-DEBUG\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.x86.chk.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x0981,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x092c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x0b60,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows2003_sp1.amd64.chk.rtm.symbols\exe\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 5,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 3790,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1f75,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x1f18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x22b4,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86fre.Symbols\EXE\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 6000,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x19c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x196c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1bac,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86fre.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 6000,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x19c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x196c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1bac,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.amd64fre.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 6000,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x3375,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x3318,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x38bc,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86chk.Symbols\EXE\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 6000,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x19c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x196c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1bac,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.x86chk.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 6000,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x19c1,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x196c,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1bac,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\WindowsVista.6000.061101-2205.amd64chk.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 6000,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x3375,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x3318,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x38bc,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86fre.Symbols\EXE\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 6001,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86fre.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 6001,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.amd64fre.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 6001,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x3475,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x3418,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x399c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86chk.Symbols\EXE\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 6001,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.x86chk.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 6001,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Longhorn.6001.080118-1840.amd64chk.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 6001,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x3475,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x3418,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x399c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86fre.Symbols\EXE\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 6002,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86fre.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 6002,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.amd64fre.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 6002,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x3475,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x3418,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x399c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86chk.Symbols\EXE\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 6002,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.x86chk.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 6002,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1a41,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x19ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x1c2c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\WindowsVista.6002.090410-1830.amd64chk.Symbols\EXE\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 2,
+ /* .uBuildNo = */ 6002,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x3475,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x3418,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x399c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86FRE.Symbols\Symbols\ntkrpamp.pdb\5B308B4ED6464159B87117C711E7340C2\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 7600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86FRE.Symbols\Symbols\ntkrnlmp.pdb\998A3472EEA6405CB8C089DE868F26222\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 7600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X64FRE.Symbols\Symbols\ntkrnlmp.pdb\F8E2A8B5C9B74BF4A6E4A48F180099942\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 7600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x21d9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2198,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x4bb8,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86CHK.Symbols\Symbols\ntkrnlmp.pdb\9E7882E37C3E4AC9BB60F4EAD9DB492A1\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 7600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X86CHK.Symbols\Symbols\ntkrpamp.pdb\3269AC66C11B41FC995991F129E95D5C1\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 7600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Win7.7600.16385.090713-1255.X64CHK.Symbols\Symbols\ntkrnlmp.pdb\C491E3167994497FA91338D08A7787041\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 7600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x21d9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2198,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x4bb8,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86FRE.Symbols\Symbols\ntkrpamp.pdb\684DA42A30CC450F81C535B4D18944B12\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 7601,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86FRE.Symbols\Symbols\ntkrnlmp.pdb\00625D7D36754CBEBA4533BA9A0F3FE22\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 7601,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.AMD64FRE.Symbols\Symbols\ntkrnlmp.pdb\3844DBB920174967BE7AA4A2C20430FA2\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 7601,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x21d9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2198,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x4bb8,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86CHK.Symbols\Symbols\ntkrpamp.pdb\C3355A163C47464183D85DE0B836F83A1\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 7601,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.X86CHK.Symbols\Symbols\ntkrnlmp.pdb\1477BEA3E003427CB248D5233B0601951\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 7601,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x1931,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x18ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x336c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Win7SP1.7601.17514.101119-1850.AMD64CHK.Symbols\Symbols\ntkrnlmp.pdb\FF0DE75C807A4B85A7668D2113A62EF11\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 1,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 1,
+ /* .uBuildNo = */ 7601,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x21d9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2198,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x4bb8,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.X86FRE.Symbols\Symbols\ntkrpamp.pdb\E2342527EA214C109CD28A19ED4FBCCE2\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9200,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2231,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x21ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x3c7c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.x64FRE.Symbols\Symbols\ntkrnlmp.pdb\724821001C1C4A03AED8C4C71C2E8D1D2\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9200,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2dd9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2d98,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x5948,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.X86CHK.Symbols\Symbols\ntkrpamp.pdb\C4F414C9D1854DE495BDAD814A722C4D1\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9200,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2231,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x21ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x3c7c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\Windows_Win8.9200.16384.120725-1247.x64CHK.Symbols\Symbols\ntkrnlmp.pdb\FC0361C3243D459496EE02EF1A7ACD271\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 2,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9200,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2dd9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2d98,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x5948,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_x86_2712593\ntkrpamp.pdb\9DC1F995475C456C8D1AA9606E3106931\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 3,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2239,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x21ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x3c7c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_x64_2712576\ntkrnlmp.pdb\A9BBA3C139724A738BE17665DB4393CA1\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 3,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2de9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2d98,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x5958,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_debug_checked_x86_2712583\ntkrpamp.pdb\77DAB075113647B5888133D3F79B7B171\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 3,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2239,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x21ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x3c7c,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\en_windows_8_1_symbols_debug_checked_x64_2712568\ntkrnlmp.pdb\4C5FFE3E839647C5B9471D0C8F9710E11\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 6,
+ /* .uMinorVer = */ 3,
+ /* .fChecked = */ true,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 9600,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2de9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2d98,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x5958,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_X86
+ { /* Source: s:\WinSyms\u\en_windows_10_symbols_x86_6903197\ntkrpamp.pdb\3A07902D18FD40CE929445D1777703241\ntkrpamp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 10,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 10240,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2239,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x21ec,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x3cfc,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\en_windows_10_symbols_x64_6903177\ntkrnlmp.pdb\C68EE22FDCF6477895C54A862BE165671\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 10,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 10240,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2de9,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2d98,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x6258,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+# ifdef RT_ARCH_AMD64
+ { /* Source: s:\WinSyms\u\en_windows_10_17134_x64_symserv\ntkrnlmp.pdb\1E2C949B928244638C2A7406B9F3824A1\ntkrnlmp.pdb */
+ /*.OsVerInfo = */
+ {
+ /* .uMajorVer = */ 10,
+ /* .uMinorVer = */ 0,
+ /* .fChecked = */ false,
+ /* .fSmp = */ true,
+ /* .uCsdNo = */ 0,
+ /* .uBuildNo = */ 17134,
+ },
+ /* .KPRCB = */
+ {
+ /* .offQuantumEnd = */ 0x2e69,
+ /* .cbQuantumEnd = */ 0x0001,
+ /* .offDpcQueueDepth = */ 0x2e18,
+ /* .cbDpcQueueDepth = */ 0x0004,
+ /* .offVendorString = */ 0x6290,
+ /* .cbVendorString = */ 0x000d,
+ },
+ },
+# endif
+};
+#endif /* !RTNTSDB_NO_DATA */
+
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_symdbdata_h */
+
diff --git a/src/VBox/Runtime/r0drv/nt/the-nt-kernel.h b/src/VBox/Runtime/r0drv/nt/the-nt-kernel.h
new file mode 100644
index 00000000..f11ef67d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/the-nt-kernel.h
@@ -0,0 +1,99 @@
+/* $Id: the-nt-kernel.h $ */
+/** @file
+ * IPRT - Include all necessary headers for the NT kernel.
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_nt_the_nt_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_nt_the_nt_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/cdefs.h>
+
+#if defined(RT_ARCH_X86) && !defined(NO_INTERLOCKED_INTRINSICS)
+# define NO_INTERLOCKED_INTRINSICS /* avoid trouble */
+#endif
+#if (_MSC_VER >= 1400) && !defined(VBOX_WITH_PATCHED_DDK)
+# include <iprt/asm.h>
+# define _InterlockedExchange _InterlockedExchange_StupidDDKVsCompilerCrap
+# define _InterlockedExchangeAdd _InterlockedExchangeAdd_StupidDDKVsCompilerCrap
+# define _InterlockedCompareExchange _InterlockedCompareExchange_StupidDDKVsCompilerCrap
+# define _InterlockedAddLargeStatistic _InterlockedAddLargeStatistic_StupidDDKVsCompilerCrap
+# pragma warning(disable : 4163)
+RT_C_DECLS_BEGIN
+# include <iprt/nt/nt.h>
+RT_C_DECLS_END
+# pragma warning(default : 4163)
+# undef _InterlockedExchange
+# undef _InterlockedExchangeAdd
+# undef _InterlockedCompareExchange
+# undef _InterlockedAddLargeStatistic
+#else
+RT_C_DECLS_BEGIN
+# include <iprt/nt/nt.h>
+RT_C_DECLS_END
+#endif
+
+#include <memory.h>
+#if !defined(RT_OS_WINDOWS)
+# error "RT_OS_WINDOWS must be defined!"
+#endif
+
+#include <iprt/param.h>
+#ifndef PAGE_OFFSET_MASK
+# define PAGE_OFFSET_MASK (PAGE_SIZE - 1)
+#endif
+
+/* Missing if we're compiling against older WDKs. */
+#ifndef NonPagedPoolNx
+# define NonPagedPoolNx ((POOL_TYPE)512)
+#endif
+
+/*
+ * When targeting NT4 we have to undo some of the nice macros
+ * installed by the later DDKs.
+ */
+#undef ExAllocatePool
+#undef ExFreePool
+
+/** @def IPRT_NT_POOL_TAG
+ * Tag to use with the NT Pool APIs.
+ * In memory and in the various windbg tools it appears in the reverse order of
+ * what it is given as here, so it'll read "IPRT".
+ */
+#define IPRT_NT_POOL_TAG 'TRPI'
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_nt_the_nt_kernel_h */
+
diff --git a/src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp
new file mode 100644
index 00000000..27d20d3d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/thread-r0drv-nt.cpp
@@ -0,0 +1,250 @@
+/* $Id: thread-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Threads, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+#include "internal-r0drv-nt.h"
+
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)PsGetCurrentThread();
+}
+
+
+static int rtR0ThreadNtSleepCommon(RTMSINTERVAL cMillies)
+{
+ LARGE_INTEGER Interval;
+ Interval.QuadPart = -(int64_t)cMillies * 10000;
+ NTSTATUS rcNt = KeDelayExecutionThread(KernelMode, TRUE, &Interval);
+ switch (rcNt)
+ {
+ case STATUS_SUCCESS:
+ return VINF_SUCCESS;
+ case STATUS_ALERTED:
+ case STATUS_USER_APC:
+ return VERR_INTERRUPTED;
+ default:
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadNtSleepCommon(cMillies);
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ return ZwYieldExecution() != STATUS_NO_YIELD_PERFORMED;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); RT_NOREF1(hThread);
+ KIRQL Irql = KeGetCurrentIrql();
+ if (Irql > APC_LEVEL)
+ return false;
+ if (!ASMIntAreEnabled())
+ return false;
+ return true;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); RT_NOREF1(hThread);
+
+ /*
+ * The KeShouldYieldProcessor API introduced in Windows 10 looks like exactly
+ * what we want. But of course there is a snag. It may return with interrupts
+ * enabled when called with them disabled. Let's just hope it doesn't get upset
+ * by disabled interrupts in other ways...
+ */
+ if (g_pfnrtKeShouldYieldProcessor)
+ {
+ RTCCUINTREG fSavedFlags = ASMGetFlags();
+ bool fReturn = g_pfnrtKeShouldYieldProcessor() != FALSE;
+ ASMSetFlags(fSavedFlags);
+ return fReturn;
+ }
+
+ /*
+ * Fallback approach for pre W10 kernels.
+ *
+ * If W10 is anything to go by, we should also check and yield when:
+ * - pPrcb->NextThread != NULL && pPrcb->NextThread != pPrcb->CurrentThread
+ * when QuantumEnd is zero.
+ * - pPrcb->DpcRequestSummary & 1
+ * - pPrcb->DpcRequestSummary & 0x1e
+ */
+
+ /*
+ * Read the globals and check if they are useful.
+ */
+/** @todo Should we check KPRCB.InterruptRequest and KPRCB.DpcInterruptRequested (older kernels). */
+ uint32_t const offQuantumEnd = g_offrtNtPbQuantumEnd;
+ uint32_t const cbQuantumEnd = g_cbrtNtPbQuantumEnd;
+ uint32_t const offDpcQueueDepth = g_offrtNtPbDpcQueueDepth;
+ if (!offQuantumEnd && !cbQuantumEnd && !offDpcQueueDepth)
+ return false;
+ Assert((offQuantumEnd && cbQuantumEnd) || (!offQuantumEnd && !cbQuantumEnd));
+
+ /*
+ * Disable interrupts so we won't be messed around.
+ */
+ bool fPending;
+ RTCCUINTREG fSavedFlags = ASMIntDisableFlags();
+
+#ifdef RT_ARCH_X86
+ PKPCR pPcr = (PKPCR)__readfsdword(RT_UOFFSETOF(KPCR,SelfPcr));
+ uint8_t *pbPrcb = (uint8_t *)pPcr->Prcb;
+
+#elif defined(RT_ARCH_AMD64)
+ /* HACK ALERT! The offset is from windbg/vista64. */
+ PKPCR pPcr = (PKPCR)__readgsqword(RT_UOFFSETOF(KPCR,Self));
+ uint8_t *pbPrcb = (uint8_t *)pPcr->CurrentPrcb;
+
+#else
+# error "port me"
+#endif
+
+ /* Check QuantumEnd. */
+ if (cbQuantumEnd == 1)
+ {
+ uint8_t volatile *pbQuantumEnd = (uint8_t volatile *)(pbPrcb + offQuantumEnd);
+ fPending = *pbQuantumEnd == TRUE;
+ }
+ else if (cbQuantumEnd == sizeof(uint32_t))
+ {
+ uint32_t volatile *pu32QuantumEnd = (uint32_t volatile *)(pbPrcb + offQuantumEnd);
+ fPending = *pu32QuantumEnd != 0;
+ }
+ else
+ fPending = false;
+
+ /* Check DpcQueueDepth. */
+ if ( !fPending
+ && offDpcQueueDepth)
+ {
+ uint32_t volatile *pu32DpcQueueDepth = (uint32_t volatile *)(pbPrcb + offDpcQueueDepth);
+ fPending = *pu32DpcQueueDepth > 0;
+ }
+
+ ASMSetFlags(fSavedFlags);
+ return fPending;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ if (g_pfnrtKeShouldYieldProcessor)
+ return true;
+#if 0 /** @todo RTThreadPreemptIsPending isn't good enough on w7 and possibly elsewhere. */
+ /* RTThreadPreemptIsPending is only reliable if we've got both offsets and size. */
+ return g_offrtNtPbQuantumEnd != 0
+ && g_cbrtNtPbQuantumEnd != 0
+ && g_offrtNtPbDpcQueueDepth != 0;
+#else
+ return false;
+#endif
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+ /* yes, kernel preemption is possible. */
+ return true;
+}
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->uchOldIrql == 255);
+ Assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &pState->uchOldIrql);
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+}
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+ KeLowerIrql(pState->uchOldIrql);
+ pState->uchOldIrql = 255;
+}
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); NOREF(hThread);
+
+ KIRQL CurIrql = KeGetCurrentIrql();
+ return CurIrql > PASSIVE_LEVEL; /** @todo Is there a more correct way? */
+}
+
+
+RTDECL(int) RTThreadQueryTerminationStatus(RTTHREAD hThread)
+{
+ AssertReturn(hThread == NIL_RTTHREAD, VERR_INVALID_HANDLE);
+ if (RT_LIKELY(g_pfnrtPsIsThreadTerminating))
+ {
+ BOOLEAN fRc = g_pfnrtPsIsThreadTerminating(PsGetCurrentThread());
+ return !fRc ? VINF_SUCCESS : VINF_THREAD_IS_TERMINATING;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp
new file mode 100644
index 00000000..b93853a5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/thread2-r0drv-nt.cpp
@@ -0,0 +1,167 @@
+/* $Id: thread2-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+
+#include <iprt/thread.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+#include "internal/thread.h"
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ /* No TLS in Ring-0. :-/ */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative((RTNATIVETHREAD)PsGetCurrentThread());
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ /*
+ * Convert the IPRT priority type to NT priority.
+ *
+ * The NT priority is in the range 0..32, with realtime starting
+ * at 16 and the default for user processes at 8. (Should try find
+ * the appropriate #defines for some of this...)
+ */
+ KPRIORITY Priority;
+ switch (enmType)
+ {
+ case RTTHREADTYPE_INFREQUENT_POLLER: Priority = 6; break;
+ case RTTHREADTYPE_EMULATION: Priority = 7; break;
+ case RTTHREADTYPE_DEFAULT: Priority = 8; break;
+ case RTTHREADTYPE_MSG_PUMP: Priority = 9; break;
+ case RTTHREADTYPE_IO: Priority = LOW_REALTIME_PRIORITY; break;
+ case RTTHREADTYPE_TIMER: Priority = MAXIMUM_PRIORITY; break;
+
+ default:
+ AssertMsgFailed(("enmType=%d\n", enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Do the actual modification.
+ */
+ /*KPRIORITY oldPririty = */KeSetPriorityThread((PKTHREAD)pThread->Core.Key, Priority);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ RT_NOREF1(pThread);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ PVOID pvThreadObj = pThread->Core.Key;
+ NTSTATUS rcNt = KeWaitForSingleObject(pvThreadObj, Executive, KernelMode, FALSE, NULL);
+ AssertMsg(rcNt == STATUS_SUCCESS, ("rcNt=%#x\n", rcNt)); RT_NOREF1(rcNt);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+/**
+ * Native kernel thread wrapper function.
+ *
+ * This will forward to rtThreadMain and do termination upon return.
+ *
+ * @param pvArg Pointer to the argument package.
+ */
+static VOID rtThreadNativeMain(PVOID pvArg)
+{
+ PETHREAD Self = PsGetCurrentThread();
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArg;
+
+ rtThreadMain(pThread, (RTNATIVETHREAD)Self, &pThread->szName[0]);
+
+ ObDereferenceObject(Self); /* the rtThreadNativeCreate ref. */
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+ /*
+ * PsCreateSysemThread create a thread an give us a handle in return.
+ * We requests the object for that handle and then close it, so what
+ * we keep around is the pointer to the thread object and not a handle.
+ * The thread will dereference the object before returning.
+ */
+ HANDLE hThread = NULL;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
+ NTSTATUS rc = PsCreateSystemThread(&hThread,
+ THREAD_ALL_ACCESS,
+ &ObjAttr,
+ NULL /* ProcessHandle - kernel */,
+ NULL /* ClientID - kernel */,
+ rtThreadNativeMain,
+ pThreadInt);
+ if (NT_SUCCESS(rc))
+ {
+ PVOID pvThreadObj;
+ rc = ObReferenceObjectByHandle(hThread, THREAD_ALL_ACCESS, NULL /* object type */,
+ KernelMode, &pvThreadObj, NULL /* handle info */);
+ if (NT_SUCCESS(rc))
+ {
+ ZwClose(hThread);
+ *pNativeThread = (RTNATIVETHREAD)pvThreadObj;
+ }
+ else
+ AssertMsgFailed(("%#x\n", rc));
+ }
+ return RTErrConvertFromNtStatus(rc);
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp
new file mode 100644
index 00000000..32d0aa74
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/time-r0drv-nt.cpp
@@ -0,0 +1,159 @@
+/* $Id: time-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, Nt.
+ */
+
+/*
+ * 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 *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include "the-nt-kernel.h"
+#include "internal-r0drv-nt.h"
+#include <iprt/time.h>
+
+
+/*
+ * The KeQueryTickCount macro isn't compatible with NT 3.1, use the
+ * exported KPI instead.
+ */
+#ifdef RT_ARCH_X86
+# undef KeQueryTickCount
+extern "C" NTKERNELAPI void NTAPI KeQueryTickCount(PLARGE_INTEGER);
+#endif
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+ /*
+ * Note! The time source we use here must be exactly the same as in
+ * the ring-3 code!
+ *
+ * Using interrupt time is the simplest and requires the least calculation.
+ * It is also accounting for suspended time. Unfortuantely, there is no
+ * ring-3 for reading it... but that won't stop us.
+ *
+ * Using the tick count is problematic in ring-3 on older windows version
+ * as we can only get the 32-bit tick value, i.e. we'll roll over sooner or
+ * later.
+ */
+#if 1
+ /* Interrupt time. */
+ LARGE_INTEGER InterruptTime;
+ if (g_pfnrtKeQueryInterruptTimePrecise)
+ {
+ ULONG64 QpcTsIgnored;
+ InterruptTime.QuadPart = g_pfnrtKeQueryInterruptTimePrecise(&QpcTsIgnored);
+ }
+# ifdef RT_ARCH_X86
+ else if (g_pfnrtKeQueryInterruptTime) /* W2K+ */
+ InterruptTime.QuadPart = g_pfnrtKeQueryInterruptTime();
+ else if (g_uRtNtVersion >= RTNT_MAKE_VERSION(3, 50))
+ {
+ /* NT 3.50 and later, also pre-init: Use the user shared data. */
+ do
+ {
+ InterruptTime.HighPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High1Time;
+ InterruptTime.LowPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.LowPart;
+ } while (((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High2Time != InterruptTime.HighPart);
+ }
+ else
+ {
+ /*
+ * There is no KUSER_SHARED_DATA structure on NT 3.1, so we have no choice
+ * but to use the tick count. We must also avoid the KeQueryTickCount macro
+ * in the WDK, since NT 3.1 doesn't have the KeTickCount data export either (see above).
+ */
+ static ULONG volatile s_uTimeIncrement = 0;
+ ULONG uTimeIncrement = s_uTimeIncrement;
+ if (!uTimeIncrement)
+ {
+ uTimeIncrement = KeQueryTimeIncrement();
+ Assert(uTimeIncrement != 0);
+ Assert(uTimeIncrement * 100 / 100 == uTimeIncrement);
+ uTimeIncrement *= 100;
+ s_uTimeIncrement = uTimeIncrement;
+ }
+
+ KeQueryTickCount(&InterruptTime);
+ return (uint64_t)InterruptTime.QuadPart * uTimeIncrement;
+ }
+# else
+ else
+ InterruptTime.QuadPart = KeQueryInterruptTime(); /* Macro on AMD64. */
+# endif
+ return (uint64_t)InterruptTime.QuadPart * 100;
+#else
+ /* Tick count. Works all the way back to NT 3.1 with #undef above. */
+ LARGE_INTEGER Tick;
+ KeQueryTickCount(&Tick);
+ return (uint64_t)Tick.QuadPart * KeQueryTimeIncrement() * 100;
+#endif
+}
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ LARGE_INTEGER SystemTime;
+ if (g_pfnrtKeQuerySystemTimePrecise)
+ g_pfnrtKeQuerySystemTimePrecise(&SystemTime);
+ else
+ KeQuerySystemTime(&SystemTime); /* Macro on AMD64, export on X86. */
+ return RTTimeSpecSetNtTime(pTime, SystemTime.QuadPart);
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp
new file mode 100644
index 00000000..c1d2f121
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/timer-r0drv-nt.cpp
@@ -0,0 +1,1102 @@
+/* $Id: timer-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - Timers, Ring-0 Driver, NT.
+ */
+
+/*
+ * 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 "the-nt-kernel.h"
+
+#include <iprt/timer.h>
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+
+#include "internal-r0drv-nt.h"
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** This seems to provide better accuracy. */
+#define RTR0TIMER_NT_MANUAL_RE_ARM 1
+
+#if !defined(IN_GUEST) || defined(DOXYGEN_RUNNING)
+/** This using high resolution timers introduced with windows 8.1. */
+# define RTR0TIMER_NT_HIGH_RES 1
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * A sub timer structure.
+ *
+ * This is used for keeping the per-cpu tick and DPC object.
+ */
+typedef struct RTTIMERNTSUBTIMER
+{
+ /** The tick counter. */
+ uint64_t iTick;
+ /** Pointer to the parent timer. */
+ PRTTIMER pParent;
+ /** Thread active executing the worker function, NIL if inactive. */
+ RTNATIVETHREAD volatile hActiveThread;
+ /** The NT DPC object. */
+ KDPC NtDpc;
+ /** Whether we failed to set the target CPU for the DPC and that this needs
+ * to be done at RTTimerStart (simple timers) or during timer callback (omni). */
+ bool fDpcNeedTargetCpuSet;
+} RTTIMERNTSUBTIMER;
+/** Pointer to a NT sub-timer structure. */
+typedef RTTIMERNTSUBTIMER *PRTTIMERNTSUBTIMER;
+
+/**
+ * The internal representation of an Linux timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Suspend count down for single shot omnit timers. */
+ int32_t volatile cOmniSuspendCountDown;
+ /** Flag indicating the timer is suspended. */
+ bool volatile fSuspended;
+ /** Whether the timer must run on one specific CPU or not. */
+ bool fSpecificCpu;
+ /** Whether the timer must run on all CPUs or not. */
+ bool fOmniTimer;
+ /** The CPU it must run on if fSpecificCpu is set.
+ * The master CPU for an omni-timer. */
+ RTCPUID idCpu;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** User argument. */
+ void *pvUser;
+
+ /** @name Periodic scheduling / RTTimerChangeInterval.
+ * @{ */
+ /** Spinlock protecting the u64NanoInterval, iMasterTick, uNtStartTime,
+ * uNtDueTime and (at least for updating) fSuspended. */
+ KSPIN_LOCK Spinlock;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t volatile u64NanoInterval;
+ /** The the current master tick. This does not necessarily follow that of
+ * the subtimer, as RTTimerChangeInterval may cause it to reset. */
+ uint64_t volatile iMasterTick;
+#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
+ /** The desired NT time of the first tick.
+ * This is not set for one-shot timers, only periodic ones. */
+ uint64_t volatile uNtStartTime;
+ /** The current due time (absolute interrupt time).
+ * This is not set for one-shot timers, only periodic ones. */
+ uint64_t volatile uNtDueTime;
+#endif
+ /** @} */
+
+ /** The NT timer object. */
+ KTIMER NtTimer;
+#ifdef RTR0TIMER_NT_HIGH_RES
+ /** High resolution timer. If not NULL, this must be used instead of NtTimer. */
+ PEX_TIMER pHighResTimer;
+#endif
+ /** The number of sub-timers. */
+ RTCPUID cSubTimers;
+ /** Sub-timers.
+ * Normally there is just one, but for RTTIMER_FLAGS_CPU_ALL this will contain
+ * an entry for all possible cpus. In that case the index will be the same as
+ * for the RTCpuSet. */
+ RTTIMERNTSUBTIMER aSubTimers[1];
+} RTTIMER;
+
+
+#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
+
+/**
+ * Get current NT interrupt time.
+ * @return NT interrupt time
+ */
+static uint64_t rtTimerNtQueryInterruptTime(void)
+{
+# ifdef RT_ARCH_AMD64
+ return KeQueryInterruptTime(); /* macro */
+# else
+ if (g_pfnrtKeQueryInterruptTime)
+ return g_pfnrtKeQueryInterruptTime();
+
+ /* NT4 */
+ ULARGE_INTEGER InterruptTime;
+ do
+ {
+ InterruptTime.HighPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High1Time;
+ InterruptTime.LowPart = ((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.LowPart;
+ } while (((KUSER_SHARED_DATA volatile *)SharedUserData)->InterruptTime.High2Time != (LONG)InterruptTime.HighPart);
+ return InterruptTime.QuadPart;
+# endif
+}
+
+/**
+ * Get current NT interrupt time, high resolution variant.
+ * @return High resolution NT interrupt time
+ */
+static uint64_t rtTimerNtQueryInterruptTimeHighRes(void)
+{
+ if (g_pfnrtKeQueryInterruptTimePrecise)
+ {
+ ULONG64 uQpcIgnored;
+ return g_pfnrtKeQueryInterruptTimePrecise(&uQpcIgnored);
+ }
+ return rtTimerNtQueryInterruptTime();
+}
+
+#endif /* RTR0TIMER_NT_MANUAL_RE_ARM */
+
+
+/**
+ * Worker for rtTimerNtRearmInternval that calculates the next due time.
+ *
+ * @returns The next due time (relative, so always negative).
+ * @param uNtNow The current time.
+ * @param uNtStartTime The start time of the timer.
+ * @param iTick The next tick number (zero being @a uNtStartTime).
+ * @param cNtInterval The timer interval in NT ticks.
+ * @param cNtNegDueSaftyMargin The due time safety margin in negative NT
+ * ticks.
+ * @param cNtMinNegInterval The minium interval to use when in catchup
+ * mode, also negative NT ticks.
+ */
+DECLINLINE(int64_t) rtTimerNtCalcNextDueTime(uint64_t uNtNow, uint64_t uNtStartTime, uint64_t iTick, uint64_t cNtInterval,
+ int32_t const cNtNegDueSaftyMargin, int32_t const cNtMinNegInterval)
+{
+ /* Calculate the actual time elapsed since timer start: */
+ int64_t iDueTime = uNtNow - uNtStartTime;
+ if (iDueTime < 0)
+ iDueTime = 0;
+
+ /* Now calculate the nominal time since timer start for the next tick: */
+ uint64_t const uNtNextRelStart = iTick * cNtInterval;
+
+ /* Calulate now much time we have to the next tick: */
+ iDueTime -= uNtNextRelStart;
+
+ /* If we haven't already overshot the due time, including some safety margin, we're good: */
+ if (iDueTime < cNtNegDueSaftyMargin)
+ return iDueTime;
+
+ /* Okay, we've overshot it and are in catchup mode: */
+ if (iDueTime < (int64_t)cNtInterval)
+ iDueTime = -(int64_t)(cNtInterval / 2); /* double time */
+ else if (iDueTime < (int64_t)(cNtInterval * 4))
+ iDueTime = -(int64_t)(cNtInterval / 4); /* quadruple time */
+ else
+ return cNtMinNegInterval;
+
+ /* Make sure we don't try intervals smaller than the minimum specified by the caller: */
+ if (iDueTime > cNtMinNegInterval)
+ iDueTime = cNtMinNegInterval;
+ return iDueTime;
+}
+
+/**
+ * Manually re-arms an internval timer.
+ *
+ * Turns out NT doesn't necessarily do a very good job at re-arming timers
+ * accurately, this is in part due to KeSetTimerEx API taking the interval in
+ * milliseconds.
+ *
+ * @param pTimer The timer.
+ * @param pMasterDpc The master timer DPC for passing to KeSetTimerEx
+ * in low-resolution mode. Ignored for high-res.
+ */
+static void rtTimerNtRearmInternval(PRTTIMER pTimer, PKDPC pMasterDpc)
+{
+#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
+ Assert(pTimer->u64NanoInterval);
+
+ /*
+ * For simplicity we acquire the spinlock for the whole operation.
+ * This should be perfectly fine as it doesn't change the IRQL.
+ */
+ Assert(KeGetCurrentIrql() >= DISPATCH_LEVEL);
+ KeAcquireSpinLockAtDpcLevel(&pTimer->Spinlock);
+
+ /*
+ * Make sure it wasn't suspended
+ */
+ if (!ASMAtomicUoReadBool(&pTimer->fSuspended))
+ {
+ uint64_t const cNtInterval = ASMAtomicUoReadU64(&pTimer->u64NanoInterval) / 100;
+ uint64_t const uNtStartTime = ASMAtomicUoReadU64(&pTimer->uNtStartTime);
+ uint64_t const iTick = ++pTimer->iMasterTick;
+
+ /*
+ * Calculate the deadline for the next timer tick and arm the timer.
+ * We always use a relative tick, i.e. negative DueTime value. This is
+ * crucial for the the high resolution API as it will bugcheck otherwise.
+ */
+ int64_t iDueTime;
+ uint64_t uNtNow;
+# ifdef RTR0TIMER_NT_HIGH_RES
+ if (pTimer->pHighResTimer)
+ {
+ /* Must use highres time here. */
+ uNtNow = rtTimerNtQueryInterruptTimeHighRes();
+ iDueTime = rtTimerNtCalcNextDueTime(uNtNow, uNtStartTime, iTick, cNtInterval,
+ -100 /* 10us safety */, -2000 /* 200us min interval*/);
+ g_pfnrtExSetTimer(pTimer->pHighResTimer, iDueTime, 0, NULL);
+ }
+ else
+# endif
+ {
+ /* Expect interrupt time and timers to expire at the same time, so
+ don't use high res time api here. */
+ uNtNow = rtTimerNtQueryInterruptTime();
+ iDueTime = rtTimerNtCalcNextDueTime(uNtNow, uNtStartTime, iTick, cNtInterval,
+ -100 /* 10us safety */, -2500 /* 250us min interval*/); /** @todo use max interval here */
+ LARGE_INTEGER DueTime;
+ DueTime.QuadPart = iDueTime;
+ KeSetTimerEx(&pTimer->NtTimer, DueTime, 0, pMasterDpc);
+ }
+
+ pTimer->uNtDueTime = uNtNow + -iDueTime;
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pTimer->Spinlock);
+#else
+ RT_NOREF(pTimer, iTick, pMasterDpc);
+#endif
+}
+
+
+/**
+ * Common timer callback worker for the non-omni timers.
+ *
+ * @param pTimer The timer.
+ */
+static void rtTimerNtSimpleCallbackWorker(PRTTIMER pTimer)
+{
+ /*
+ * Check that we haven't been suspended before doing the callout.
+ */
+ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ ASMAtomicWriteHandle(&pTimer->aSubTimers[0].hActiveThread, RTThreadNativeSelf());
+
+ if (!pTimer->u64NanoInterval)
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+ uint64_t iTick = ++pTimer->aSubTimers[0].iTick;
+
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, iTick);
+
+ /* We re-arm the timer after calling pfnTimer, as it may stop the timer
+ or change the interval, which would mean doing extra work. */
+ if (!pTimer->fSuspended && pTimer->u64NanoInterval)
+ rtTimerNtRearmInternval(pTimer, &pTimer->aSubTimers[0].NtDpc);
+
+ ASMAtomicWriteHandle(&pTimer->aSubTimers[0].hActiveThread, NIL_RTNATIVETHREAD);
+ }
+}
+
+
+/**
+ * Timer callback function for the low-resolution non-omni timers.
+ *
+ * @param pDpc Pointer to the DPC.
+ * @param pvUser Pointer to our internal timer structure.
+ * @param SystemArgument1 Some system argument.
+ * @param SystemArgument2 Some system argument.
+ */
+static void _stdcall rtTimerNtSimpleCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvUser;
+ AssertPtr(pTimer);
+#ifdef RT_STRICT
+ if (KeGetCurrentIrql() < DISPATCH_LEVEL)
+ RTAssertMsg2Weak("rtTimerNtSimpleCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
+#endif
+
+ rtTimerNtSimpleCallbackWorker(pTimer);
+
+ RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
+}
+
+
+#ifdef RTR0TIMER_NT_HIGH_RES
+/**
+ * Timer callback function for the high-resolution non-omni timers.
+ *
+ * @param pExTimer The windows timer.
+ * @param pvUser Pointer to our internal timer structure.
+ */
+static void _stdcall rtTimerNtHighResSimpleCallback(PEX_TIMER pExTimer, void *pvUser)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvUser;
+ AssertPtr(pTimer);
+ Assert(pTimer->pHighResTimer == pExTimer);
+# ifdef RT_STRICT
+ if (KeGetCurrentIrql() < DISPATCH_LEVEL)
+ RTAssertMsg2Weak("rtTimerNtHighResSimpleCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
+# endif
+
+ /* If we're not on the desired CPU, trigger the DPC. That will rearm the
+ timer and such. */
+ if ( !pTimer->fSpecificCpu
+ || pTimer->idCpu == RTMpCpuId())
+ rtTimerNtSimpleCallbackWorker(pTimer);
+ else
+ KeInsertQueueDpc(&pTimer->aSubTimers[0].NtDpc, 0, 0);
+
+ RT_NOREF(pExTimer);
+}
+#endif /* RTR0TIMER_NT_HIGH_RES */
+
+
+/**
+ * The slave DPC callback for an omni timer.
+ *
+ * @param pDpc The DPC object.
+ * @param pvUser Pointer to the sub-timer.
+ * @param SystemArgument1 Some system stuff.
+ * @param SystemArgument2 Some system stuff.
+ */
+static void _stdcall rtTimerNtOmniSlaveCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
+{
+ PRTTIMERNTSUBTIMER pSubTimer = (PRTTIMERNTSUBTIMER)pvUser;
+ PRTTIMER pTimer = pSubTimer->pParent;
+
+ AssertPtr(pTimer);
+#ifdef RT_STRICT
+ if (KeGetCurrentIrql() < DISPATCH_LEVEL)
+ RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
+ int iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId());
+ if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf)
+ RTAssertMsg2Weak("rtTimerNtOmniSlaveCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]);
+#endif
+
+ /*
+ * Check that we haven't been suspended before doing the callout.
+ */
+ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ ASMAtomicWriteHandle(&pSubTimer->hActiveThread, RTThreadNativeSelf());
+
+ if (!pTimer->u64NanoInterval)
+ if (ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown) <= 0)
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
+
+ ASMAtomicWriteHandle(&pSubTimer->hActiveThread, NIL_RTNATIVETHREAD);
+ }
+
+ NOREF(pDpc); NOREF(SystemArgument1); NOREF(SystemArgument2);
+}
+
+
+/**
+ * Called when we have an impcomplete DPC object.
+ *
+ * @returns KeInsertQueueDpc return value.
+ * @param pSubTimer The sub-timer to queue an DPC for.
+ * @param iCpu The CPU set index corresponding to that sub-timer.
+ */
+DECL_NO_INLINE(static, BOOLEAN) rtTimerNtOmniQueueDpcSlow(PRTTIMERNTSUBTIMER pSubTimer, int iCpu)
+{
+ int rc = rtMpNtSetTargetProcessorDpc(&pSubTimer->NtDpc, RTMpCpuIdFromSetIndex(iCpu));
+ if (RT_SUCCESS(rc))
+ {
+ pSubTimer->fDpcNeedTargetCpuSet = false;
+ return KeInsertQueueDpc(&pSubTimer->NtDpc, 0, 0);
+ }
+ return FALSE;
+}
+
+
+/**
+ * Wrapper around KeInsertQueueDpc that makes sure the target CPU has been set.
+ *
+ * This is for handling deferred rtMpNtSetTargetProcessorDpc failures during
+ * creation. These errors happens for offline CPUs which probably never every
+ * will come online, as very few systems do CPU hotplugging.
+ *
+ * @returns KeInsertQueueDpc return value.
+ * @param pSubTimer The sub-timer to queue an DPC for.
+ * @param iCpu The CPU set index corresponding to that sub-timer.
+ */
+DECLINLINE(BOOLEAN) rtTimerNtOmniQueueDpc(PRTTIMERNTSUBTIMER pSubTimer, int iCpu)
+{
+ if (RT_LIKELY(!pSubTimer->fDpcNeedTargetCpuSet))
+ return KeInsertQueueDpc(&pSubTimer->NtDpc, 0, 0);
+ return rtTimerNtOmniQueueDpcSlow(pSubTimer, iCpu);
+}
+
+
+/**
+ * Common timer callback worker for omni-timers.
+ *
+ * This is responsible for queueing the DPCs for the other CPUs and
+ * perform the callback on the CPU on which it is called.
+ *
+ * @param pTimer The timer.
+ * @param pSubTimer The sub-timer of the calling CPU.
+ * @param iCpuSelf The set index of the CPU we're running on.
+ */
+static void rtTimerNtOmniMasterCallbackWorker(PRTTIMER pTimer, PRTTIMERNTSUBTIMER pSubTimer, int iCpuSelf)
+{
+ /*
+ * Check that we haven't been suspended before scheduling the other DPCs
+ * and doing the callout.
+ */
+ if ( !ASMAtomicUoReadBool(&pTimer->fSuspended)
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ RTCPUSET OnlineSet;
+ RTMpGetOnlineSet(&OnlineSet);
+
+ ASMAtomicWriteHandle(&pSubTimer->hActiveThread, RTThreadNativeSelf());
+
+ if (pTimer->u64NanoInterval)
+ {
+ /*
+ * Recurring timer.
+ */
+ for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
+ if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)
+ && iCpuSelf != iCpu)
+ rtTimerNtOmniQueueDpc(&pTimer->aSubTimers[iCpu], iCpu);
+
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
+
+ /* We re-arm the timer after calling pfnTimer, as it may stop the timer
+ or change the interval, which would mean doing extra work. */
+ if (!pTimer->fSuspended && pTimer->u64NanoInterval)
+ rtTimerNtRearmInternval(pTimer, &pSubTimer->NtDpc);
+ }
+ else
+ {
+ /*
+ * Single shot timers gets complicated wrt to fSuspended maintance.
+ */
+ uint32_t cCpus = 0;
+ for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
+ if (RTCpuSetIsMemberByIndex(&OnlineSet, iCpu))
+ cCpus++;
+ ASMAtomicAddS32(&pTimer->cOmniSuspendCountDown, cCpus); /** @todo this is bogus bogus bogus. The counter is only used here. */
+
+ for (int iCpu = 0; iCpu < RTCPUSET_MAX_CPUS; iCpu++)
+ if ( RTCpuSetIsMemberByIndex(&OnlineSet, iCpu)
+ && iCpuSelf != iCpu)
+ if (!rtTimerNtOmniQueueDpc(&pTimer->aSubTimers[iCpu], iCpu))
+ ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown); /* already queued and counted. */
+
+ if (ASMAtomicDecS32(&pTimer->cOmniSuspendCountDown) <= 0)
+ ASMAtomicWriteBool(&pTimer->fSuspended, true);
+
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pSubTimer->iTick);
+ }
+
+ ASMAtomicWriteHandle(&pSubTimer->hActiveThread, NIL_RTNATIVETHREAD);
+ }
+}
+
+
+/**
+ * The timer callback for an omni-timer, low-resolution.
+ *
+ * @param pDpc The DPC object.
+ * @param pvUser Pointer to the sub-timer.
+ * @param SystemArgument1 Some system stuff.
+ * @param SystemArgument2 Some system stuff.
+ */
+static void _stdcall rtTimerNtOmniMasterCallback(IN PKDPC pDpc, IN PVOID pvUser, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
+{
+ PRTTIMERNTSUBTIMER const pSubTimer = (PRTTIMERNTSUBTIMER)pvUser;
+ PRTTIMER const pTimer = pSubTimer->pParent;
+ RTCPUID idCpu = RTMpCpuId();
+ int const iCpuSelf = RTMpCpuIdToSetIndex(idCpu);
+
+ AssertPtr(pTimer);
+#ifdef RT_STRICT
+ if (KeGetCurrentIrql() < DISPATCH_LEVEL)
+ RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
+ /* We must be called on the master CPU or the tick variable goes south. */
+ if (pSubTimer - &pTimer->aSubTimers[0] != iCpuSelf)
+ RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: iCpuSelf=%d pSubTimer=%p / %d\n", iCpuSelf, pSubTimer, pSubTimer - &pTimer->aSubTimers[0]);
+ if (pTimer->idCpu != idCpu)
+ RTAssertMsg2Weak("rtTimerNtOmniMasterCallback: pTimer->idCpu=%d vs idCpu=%d\n", pTimer->idCpu, idCpu);
+#endif
+
+ rtTimerNtOmniMasterCallbackWorker(pTimer, pSubTimer, iCpuSelf);
+
+ RT_NOREF(pDpc, SystemArgument1, SystemArgument2);
+}
+
+
+#ifdef RTR0TIMER_NT_HIGH_RES
+/**
+ * The timer callback for an high-resolution omni-timer.
+ *
+ * @param pExTimer The windows timer.
+ * @param pvUser Pointer to our internal timer structure.
+ */
+static void __stdcall rtTimerNtHighResOmniCallback(PEX_TIMER pExTimer, void *pvUser)
+{
+ PRTTIMER const pTimer = (PRTTIMER)pvUser;
+ int const iCpuSelf = RTMpCpuIdToSetIndex(RTMpCpuId());
+ PRTTIMERNTSUBTIMER const pSubTimer = &pTimer->aSubTimers[iCpuSelf];
+
+ AssertPtr(pTimer);
+ Assert(pTimer->pHighResTimer == pExTimer);
+# ifdef RT_STRICT
+ if (KeGetCurrentIrql() < DISPATCH_LEVEL)
+ RTAssertMsg2Weak("rtTimerNtHighResOmniCallback: Irql=%d expected >=%d\n", KeGetCurrentIrql(), DISPATCH_LEVEL);
+# endif
+
+ rtTimerNtOmniMasterCallbackWorker(pTimer, pSubTimer, iCpuSelf);
+
+ RT_NOREF(pExTimer);
+}
+#endif /* RTR0TIMER_NT_HIGH_RES */
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * The operation is protected by the spinlock.
+ */
+ KIRQL bSavedIrql;
+ KeAcquireSpinLock(&pTimer->Spinlock, &bSavedIrql);
+
+ /*
+ * Check the state.
+ */
+ if (ASMAtomicUoReadBool(&pTimer->fSuspended))
+ { /* likely */ }
+ else
+ {
+ KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql);
+ return VERR_TIMER_ACTIVE;
+ }
+ if ( !pTimer->fSpecificCpu
+ || RTMpIsCpuOnline(pTimer->idCpu))
+ { /* likely */ }
+ else
+ {
+ KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql);
+ return VERR_CPU_OFFLINE;
+ }
+
+ /*
+ * Lazy set the DPC target CPU if needed.
+ */
+ if ( !pTimer->fSpecificCpu
+ || !pTimer->aSubTimers[0].fDpcNeedTargetCpuSet)
+ { /* likely */ }
+ else
+ {
+ int rc = rtMpNtSetTargetProcessorDpc(&pTimer->aSubTimers[0].NtDpc, pTimer->idCpu);
+ if (RT_FAILURE(rc))
+ {
+ KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql);
+ return rc;
+ }
+ }
+
+ /*
+ * Do the starting.
+ */
+#ifndef RTR0TIMER_NT_MANUAL_RE_ARM
+ /* Calculate the interval time: */
+ uint64_t u64Interval = pTimer->u64NanoInterval / 1000000; /* This is ms, believe it or not. */
+ ULONG ulInterval = (ULONG)u64Interval;
+ if (ulInterval != u64Interval)
+ ulInterval = MAXLONG;
+ else if (!ulInterval && pTimer->u64NanoInterval)
+ ulInterval = 1;
+#endif
+
+ /* Translate u64First to a DueTime: */
+ LARGE_INTEGER DueTime;
+ DueTime.QuadPart = -(int64_t)(u64First / 100); /* Relative, NT time. */
+ if (!DueTime.QuadPart)
+ DueTime.QuadPart = -10; /* 1us */
+
+ /* Reset tick counters: */
+ unsigned cSubTimers = pTimer->fOmniTimer ? pTimer->cSubTimers : 1;
+ for (unsigned iCpu = 0; iCpu < cSubTimers; iCpu++)
+ pTimer->aSubTimers[iCpu].iTick = 0;
+ pTimer->iMasterTick = 0;
+
+ /* Update timer state: */
+#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
+ if (pTimer->u64NanoInterval > 0)
+ {
+#ifdef RTR0TIMER_NT_HIGH_RES
+ uint64_t const uNtNow = pTimer->pHighResTimer ? rtTimerNtQueryInterruptTimeHighRes() : rtTimerNtQueryInterruptTime();
+# else
+ uint64_t const uNtNow = rtTimerNtQueryInterruptTime();
+# endif
+ pTimer->uNtStartTime = uNtNow + -DueTime.QuadPart;
+ pTimer->uNtDueTime = pTimer->uNtStartTime;
+ }
+#endif
+ pTimer->cOmniSuspendCountDown = 0;
+ ASMAtomicWriteBool(&pTimer->fSuspended, false);
+
+ /*
+ * Finally start the NT timer.
+ *
+ * We do this without holding the spinlock to err on the side of
+ * caution in case ExSetTimer or KeSetTimerEx ever should have the idea
+ * of running the callback before returning.
+ */
+ KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql);
+
+#ifdef RTR0TIMER_NT_HIGH_RES
+ if (pTimer->pHighResTimer)
+ {
+# ifdef RTR0TIMER_NT_MANUAL_RE_ARM
+ g_pfnrtExSetTimer(pTimer->pHighResTimer, DueTime.QuadPart, 0, NULL);
+# else
+ g_pfnrtExSetTimer(pTimer->pHighResTimer, DueTime.QuadPart, RT_MIN(pTimer->u64NanoInterval / 100, MAXLONG), NULL);
+# endif
+ }
+ else
+#endif
+ {
+ PKDPC const pMasterDpc = &pTimer->aSubTimers[pTimer->fOmniTimer ? RTMpCpuIdToSetIndex(pTimer->idCpu) : 0].NtDpc;
+#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
+ KeSetTimerEx(&pTimer->NtTimer, DueTime, 0, pMasterDpc);
+#else
+ KeSetTimerEx(&pTimer->NtTimer, DueTime, ulInterval, pMasterDpc);
+#endif
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker function that stops an active timer.
+ *
+ * Shared by RTTimerStop and RTTimerDestroy.
+ *
+ * @param pTimer The active timer.
+ */
+static int rtTimerNtStopWorker(PRTTIMER pTimer)
+{
+ /*
+ * Update the state from with the spinlock context.
+ */
+ KIRQL bSavedIrql;
+ KeAcquireSpinLock(&pTimer->Spinlock, &bSavedIrql);
+
+ bool const fWasSuspended = ASMAtomicXchgBool(&pTimer->fSuspended, true);
+
+ KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql);
+ if (!fWasSuspended)
+ {
+ /*
+ * We should cacnel the timer and dequeue DPCs.
+ */
+#ifdef RTR0TIMER_NT_HIGH_RES
+ if (pTimer->pHighResTimer)
+ {
+ g_pfnrtExCancelTimer(pTimer->pHighResTimer, NULL);
+
+ /* We can skip the DPC stuff, unless this is an omni timer or for a specific CPU. */
+ if (!pTimer->fSpecificCpu && !pTimer->fOmniTimer)
+ return VINF_SUCCESS;
+ }
+ else
+#endif
+ KeCancelTimer(&pTimer->NtTimer);
+
+ for (RTCPUID iCpu = 0; iCpu < pTimer->cSubTimers; iCpu++)
+ KeRemoveQueueDpc(&pTimer->aSubTimers[iCpu].NtDpc);
+ return VINF_SUCCESS;
+ }
+ return VERR_TIMER_SUSPENDED;
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Call the worker we share with RTTimerDestroy.
+ */
+ return rtTimerNtStopWorker(pTimer);
+}
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * We do all the state changes while holding the spinlock.
+ */
+ int rc = VINF_SUCCESS;
+ KIRQL bSavedIrql;
+ KeAcquireSpinLock(&pTimer->Spinlock, &bSavedIrql);
+
+ /*
+ * When the timer isn't running, this is an simple job:
+ */
+ if (!ASMAtomicUoReadBool(&pTimer->fSuspended))
+ pTimer->u64NanoInterval = u64NanoInterval;
+ else
+ {
+ /*
+ * We only implement changing the interval in RTR0TIMER_NT_MANUAL_RE_ARM
+ * mode right now. We typically let the new interval take effect after
+ * the next timer callback, unless that's too far ahead.
+ */
+#ifdef RTR0TIMER_NT_MANUAL_RE_ARM
+ pTimer->u64NanoInterval = u64NanoInterval;
+ pTimer->iMasterTick = 0;
+# ifdef RTR0TIMER_NT_HIGH_RES
+ uint64_t const uNtNow = pTimer->pHighResTimer ? rtTimerNtQueryInterruptTimeHighRes() : rtTimerNtQueryInterruptTime();
+# else
+ uint64_t const uNtNow = rtTimerNtQueryInterruptTime();
+# endif
+ if (uNtNow >= pTimer->uNtDueTime)
+ pTimer->uNtStartTime = uNtNow;
+ else
+ {
+ pTimer->uNtStartTime = pTimer->uNtDueTime;
+
+ /*
+ * Re-arm the timer if the next DueTime is both more than 1.25 new
+ * intervals and at least 0.5 ms ahead.
+ */
+ uint64_t cNtToNext = pTimer->uNtDueTime - uNtNow;
+ if ( cNtToNext >= RT_NS_1MS / 2 / 100 /* 0.5 ms */
+ && cNtToNext * 100 > u64NanoInterval + u64NanoInterval / 4)
+ {
+ pTimer->uNtStartTime = pTimer->uNtDueTime = uNtNow + u64NanoInterval / 100;
+# ifdef RTR0TIMER_NT_HIGH_RES
+ if (pTimer->pHighResTimer)
+ g_pfnrtExSetTimer(pTimer->pHighResTimer, -(int64_t)u64NanoInterval / 100, 0, NULL);
+ else
+# endif
+ {
+ LARGE_INTEGER DueTime;
+ DueTime.QuadPart = -(int64_t)u64NanoInterval / 100;
+ KeSetTimerEx(&pTimer->NtTimer, DueTime, 0,
+ &pTimer->aSubTimers[pTimer->fOmniTimer ? RTMpCpuIdToSetIndex(pTimer->idCpu) : 0].NtDpc);
+ }
+ }
+ }
+#else
+ rc = VERR_NOT_SUPPORTED;
+#endif
+ }
+
+ KeReleaseSpinLock(&pTimer->Spinlock, bSavedIrql);
+
+ return rc;
+}
+
+
+RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ /* It's ok to pass NULL pointer. */
+ if (pTimer == /*NIL_RTTIMER*/ NULL)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * We do not support destroying a timer from the callback because it is
+ * not 101% safe since we cannot flush DPCs. Solaris has the same restriction.
+ */
+ AssertReturn(KeGetCurrentIrql() == PASSIVE_LEVEL, VERR_INVALID_CONTEXT);
+
+ /*
+ * Invalidate the timer, stop it if it's running and finally free up the memory.
+ */
+ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+ rtTimerNtStopWorker(pTimer);
+
+#ifdef RTR0TIMER_NT_HIGH_RES
+ /*
+ * Destroy the high-resolution timer before flushing DPCs.
+ */
+ if (pTimer->pHighResTimer)
+ {
+ g_pfnrtExDeleteTimer(pTimer->pHighResTimer, TRUE /*fCancel*/, TRUE /*fWait*/, NULL);
+ pTimer->pHighResTimer = NULL;
+ }
+#endif
+
+ /*
+ * Flush DPCs to be on the safe side.
+ */
+ if (g_pfnrtNtKeFlushQueuedDpcs)
+ g_pfnrtNtKeFlushQueuedDpcs();
+
+ RTMemFree(pTimer);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ *ppTimer = NULL;
+
+ /*
+ * Validate flags.
+ */
+ if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
+ return VERR_INVALID_FLAGS;
+ if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
+ && !RTMpIsCpuPossible(RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK)))
+ return VERR_CPU_NOT_FOUND;
+
+ /*
+ * Allocate the timer handler.
+ */
+ RTCPUID cSubTimers = 1;
+ if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL)
+ {
+ cSubTimers = RTMpGetMaxCpuId() + 1;
+ Assert(cSubTimers <= RTCPUSET_MAX_CPUS); /* On Windows we have a 1:1 relationship between cpuid and set index. */
+ }
+
+ PRTTIMER pTimer = (PRTTIMER)RTMemAllocZ(RT_UOFFSETOF_DYN(RTTIMER, aSubTimers[cSubTimers]));
+ if (!pTimer)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize it.
+ *
+ * Note! The difference between a SynchronizationTimer and a NotificationTimer
+ * (KeInitializeTimer) is, as far as I can gather, only that the former
+ * will wake up exactly one waiting thread and the latter will wake up
+ * everyone. Since we don't do any waiting on the NtTimer, that is not
+ * relevant to us.
+ */
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->cOmniSuspendCountDown = 0;
+ pTimer->fSuspended = true;
+ pTimer->fSpecificCpu = (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL;
+ pTimer->fOmniTimer = (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL;
+ pTimer->idCpu = pTimer->fSpecificCpu ? RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK) : NIL_RTCPUID;
+ pTimer->cSubTimers = cSubTimers;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ KeInitializeSpinLock(&pTimer->Spinlock);
+ pTimer->u64NanoInterval = u64NanoInterval;
+
+ int rc = VINF_SUCCESS;
+#ifdef RTR0TIMER_NT_HIGH_RES
+ if ( (fFlags & RTTIMER_FLAGS_HIGH_RES)
+ && RTTimerCanDoHighResolution())
+ {
+ pTimer->pHighResTimer = g_pfnrtExAllocateTimer(pTimer->fOmniTimer ? rtTimerNtHighResOmniCallback
+ : rtTimerNtHighResSimpleCallback, pTimer,
+ EX_TIMER_HIGH_RESOLUTION | EX_TIMER_NOTIFICATION);
+ if (!pTimer->pHighResTimer)
+ rc = VERR_OUT_OF_RESOURCES;
+ }
+ else
+#endif
+ {
+ if (g_pfnrtKeInitializeTimerEx) /** @todo just call KeInitializeTimer. */
+ g_pfnrtKeInitializeTimerEx(&pTimer->NtTimer, SynchronizationTimer);
+ else
+ KeInitializeTimer(&pTimer->NtTimer);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ RTCPUSET OnlineSet;
+ RTMpGetOnlineSet(&OnlineSet);
+
+ if (pTimer->fOmniTimer)
+ {
+ /*
+ * Initialize the per-cpu "sub-timers", select the first online cpu to be
+ * the master. This ASSUMES that no cpus will ever go offline.
+ *
+ * Note! For the high-resolution scenario, all DPC callbacks are slaves as
+ * we have a dedicated timer callback, set above during allocation,
+ * and don't control which CPU it (rtTimerNtHighResOmniCallback) is
+ * called on.
+ */
+ pTimer->iMasterTick = 0;
+ pTimer->idCpu = NIL_RTCPUID;
+ for (unsigned iCpu = 0; iCpu < cSubTimers; iCpu++)
+ {
+ pTimer->aSubTimers[iCpu].iTick = 0;
+ pTimer->aSubTimers[iCpu].pParent = pTimer;
+
+ if ( pTimer->idCpu == NIL_RTCPUID
+ && RTCpuSetIsMemberByIndex(&OnlineSet, iCpu))
+ {
+ pTimer->idCpu = RTMpCpuIdFromSetIndex(iCpu);
+#ifdef RTR0TIMER_NT_HIGH_RES
+ if (pTimer->pHighResTimer)
+ KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniSlaveCallback, &pTimer->aSubTimers[iCpu]);
+ else
+#endif
+ KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniMasterCallback, &pTimer->aSubTimers[iCpu]);
+ }
+ else
+ KeInitializeDpc(&pTimer->aSubTimers[iCpu].NtDpc, rtTimerNtOmniSlaveCallback, &pTimer->aSubTimers[iCpu]);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&pTimer->aSubTimers[iCpu].NtDpc, HighImportance);
+
+ /* This does not necessarily work for offline CPUs that could potentially be onlined
+ at runtime, so postpone it. (See troubles on testboxmem1 after r148799.) */
+ int rc2 = rtMpNtSetTargetProcessorDpc(&pTimer->aSubTimers[iCpu].NtDpc, iCpu);
+ if (RT_SUCCESS(rc2))
+ pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = false;
+ else if (!RTCpuSetIsMemberByIndex(&OnlineSet, iCpu))
+ pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = true;
+ else
+ {
+ rc = rc2;
+ break;
+ }
+ }
+ Assert(pTimer->idCpu != NIL_RTCPUID);
+ }
+ else
+ {
+ /*
+ * Initialize the first "sub-timer", target the DPC on a specific processor
+ * if requested to do so.
+ */
+ pTimer->iMasterTick = 0;
+ pTimer->aSubTimers[0].iTick = 0;
+ pTimer->aSubTimers[0].pParent = pTimer;
+
+ KeInitializeDpc(&pTimer->aSubTimers[0].NtDpc, rtTimerNtSimpleCallback, pTimer);
+ if (g_pfnrtKeSetImportanceDpc)
+ g_pfnrtKeSetImportanceDpc(&pTimer->aSubTimers[0].NtDpc, HighImportance);
+ if (pTimer->fSpecificCpu)
+ {
+ /* This does not necessarily work for offline CPUs that could potentially be onlined
+ at runtime, so postpone it. (See troubles on testboxmem1 after r148799.) */
+ int rc2 = rtMpNtSetTargetProcessorDpc(&pTimer->aSubTimers[0].NtDpc, pTimer->idCpu);
+ if (RT_SUCCESS(rc2))
+ pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = false;
+ else if (!RTCpuSetIsMember(&OnlineSet, pTimer->idCpu))
+ pTimer->aSubTimers[0].fDpcNeedTargetCpuSet = true;
+ else
+ rc = rc2;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+ }
+
+#ifdef RTR0TIMER_NT_HIGH_RES
+ if (pTimer->pHighResTimer)
+ {
+ g_pfnrtExDeleteTimer(pTimer->pHighResTimer, FALSE, FALSE, NULL);
+ pTimer->pHighResTimer = NULL;
+ }
+#endif
+ }
+
+ RTMemFree(pTimer);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
+{
+ if (!g_pfnrtNtExSetTimerResolution)
+ return VERR_NOT_SUPPORTED;
+
+ ULONG ulGranted = g_pfnrtNtExSetTimerResolution(u32Request / 100, TRUE);
+ if (pu32Granted)
+ *pu32Granted = ulGranted * 100; /* NT -> ns */
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
+{
+ if (!g_pfnrtNtExSetTimerResolution)
+ return VERR_NOT_SUPPORTED;
+
+ g_pfnrtNtExSetTimerResolution(0 /* ignored */, FALSE);
+ NOREF(u32Granted);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTTimerCanDoHighResolution(void)
+{
+#ifdef RTR0TIMER_NT_HIGH_RES
+ return g_pfnrtExAllocateTimer != NULL
+ && g_pfnrtExDeleteTimer != NULL
+ && g_pfnrtExSetTimer != NULL
+ && g_pfnrtExCancelTimer != NULL;
+#else
+ return false;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm b/src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm
new file mode 100644
index 00000000..eeb0045f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/toxic-chkstk-r0drv-nt.asm
@@ -0,0 +1,52 @@
+; $Id: toxic-chkstk-r0drv-nt.asm $
+;; @file
+; IPRT - Toxic _chkstk symbol.
+;
+
+;
+; 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
+
+;;
+; Bad function to drag into kernel code as you're eating up too much stack.
+;
+BEGINPROC _chkstk
+%define MY_SYM _chkstk_is_considered_toxic_in_kernel_code__you_should_locate_code_using_too_much_stack_and_change_it_to_use_heap
+ extern MY_SYM
+ jmp MY_SYM
+ENDPROC _chkstk
+
diff --git a/src/VBox/Runtime/r0drv/os2/Makefile.kup b/src/VBox/Runtime/r0drv/os2/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/os2/RTR0AssertPanicSystem-r0drv-os2.asm b/src/VBox/Runtime/r0drv/os2/RTR0AssertPanicSystem-r0drv-os2.asm
new file mode 100644
index 00000000..a42f3762
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/RTR0AssertPanicSystem-r0drv-os2.asm
@@ -0,0 +1,138 @@
+; $Id: RTR0AssertPanicSystem-r0drv-os2.asm $
+;; @file
+; IPRT - RTR0AssertPanicSystem, Ring-0 Driver, OS/2.
+;
+
+;
+; Contributed by knut st. osmundsen.
+;
+; 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 code is based on:
+;
+; Copyright (c) 1999-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"
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+%define DevHlp_InternalError 02bh
+
+
+;*******************************************************************************
+;* External Symbols *
+;*******************************************************************************
+extern KernThunkStackTo16
+extern KernThunkStackTo32
+extern NAME(g_szRTAssertMsg)
+extern NAME(g_cchRTAssertMsg)
+extern NAME(g_fpfnDevHlp)
+
+
+BEGINCODE
+
+BEGINPROC_EXPORTED RTR0AssertPanicSystem
+ push ebp
+ mov ebp, esp
+ push esi
+ push edi
+ push ds
+
+ ;
+ ; Try detect debug kernel... later one day.
+ ;
+
+
+ ;
+ ; Raise an IPE.
+ ;
+ call KernThunkStackTo16
+ ;jmp far dword NAME(RTR0AssertPanicSystem_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(RTR0AssertPanicSystem_16) wrt CODE16
+ dw CODE16
+BEGINCODE16
+GLOBALNAME RTR0AssertPanicSystem_16
+ ; mov ax, seg NAME(g_szRTAssertMsg) - makes wlink crash.
+ mov ax, DATA16
+ mov ds, ax
+ mov si, NAME(g_szRTAssertMsg)
+ mov di, [NAME(g_cchRTAssertMsg)]
+ mov dl, DevHlp_InternalError
+ call far [NAME(g_fpfnDevHlp)]
+
+ ; Doesn't normally return, but in case it does...
+ ;jmp far dword NAME(RTR0AssertPanicSystem_32)
+ db 066h
+ db 0eah
+ dd NAME(RTR0AssertPanicSystem_32)
+ dw TEXT32 wrt FLAT
+BEGINCODE32:
+GLOBALNAME RTR0AssertPanicSystem_32
+ call KernThunkStackTo32
+ mov eax, 1
+ pop ds
+ pop edi
+ pop esi
+ leave
+ ret
+ENDPROC RTR0AssertPanicSystem
+
diff --git a/src/VBox/Runtime/r0drv/os2/RTR0Os2DHQueryDOSVar.asm b/src/VBox/Runtime/r0drv/os2/RTR0Os2DHQueryDOSVar.asm
new file mode 100644
index 00000000..681d77eb
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/RTR0Os2DHQueryDOSVar.asm
@@ -0,0 +1,217 @@
+; $Id: RTR0Os2DHQueryDOSVar.asm $
+;; @file
+; IPRT - DevHelp_GetDOSVar, Ring-0 Driver, OS/2.
+;
+
+;
+; Contributed by knut st. osmundsen.
+;
+; 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 code is based on:
+;
+; Copyright (c) 1999-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"
+%include "iprt/err.mac"
+
+
+;*******************************************************************************
+;* External Symbols *
+;*******************************************************************************
+extern KernThunkStackTo32
+extern KernThunkStackTo16
+extern NAME(g_fpfnDevHlp)
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+%define DevHlp_GetDOSVar 24h
+
+
+BEGINCODE
+
+;
+; Jump table used by RTR0Os2DHQueryDOSVar
+;
+DosVarJumpTab:
+ dd 0 ; 0 - Reserved
+ dd Load1600 ; 1 - GIS
+ dd Load1616 ; 2 - LIS
+ dd 0 ; 3 - Reserved
+ dd Load1616 ; 4 - VectorSDF
+ dd Load1616 ; 5 - VectorReboot
+ dd Load1616 ; 6 - VectorMSATS
+ dd AsIs ; 7 - YieldFlag (Resched)
+ dd AsIs ; 8 - TCYieldFlag (TCResched)
+ dd AsIs ; 9 - DOSTable
+ dd Load1616 ; a - VectorDEKKO
+ dd AsIs ; b - CodePgBuff
+ dd Load1616 ; c - VectorRIPL
+ dd AsIs ; d - InterruptLevel
+ dd AsIs ; e - DevClassTables
+ dd AsIs ; f - DMQS_Sel
+ dd AsIs ;10 - APMInfo
+ dd LoadWord ;11 - APM_Length (length of above structure)
+DosVarJumpTabEnd:
+%define DosVarJumpTabSize (DosVarJumpTabEnd - DosVarJumpTab) / 4
+
+;;
+; Unified DevHelp_GetDOSVar -> Far 16:16 pointer wrapper.
+;
+; @param iVar [ebp + 08h] Variable.
+; @param iMember [ebp + 0ch] Member.
+; @param pfp [ebp + 10h] Where to store the variable address (pointer to 16:16).
+;
+BEGINPROC_EXPORTED RTR0Os2DHQueryDOSVar
+ ; switch stack first.
+ call KernThunkStackTo16
+
+ ; normal prolog.
+ push ebp
+ mov ebp, esp
+ push dword [NAME(g_fpfnDevHlp)] ; ebp - 4
+ push ebx ; save ebx
+ push es ; save es
+
+ ; setup the devhelp call and switch to
+ mov eax, [ebp + 08h] ; iVar (8-bit)
+ mov ecx, [ebp + 0ch] ; iMember (16-bit)
+ mov dl, DevHlp_GetDOSVar
+
+ ; jump to the 16-bit code.
+ ;jmp far dword NAME(RTR0Os2DHQueryDOSVar_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(RTR0Os2DHQueryDOSVar_16) wrt CODE16
+ dw CODE16
+BEGINCODE16
+GLOBALNAME RTR0Os2DHQueryDOSVar_16
+ call far [ss:ebp - 4]
+
+ ;jmp far dword NAME(RTR0Os2DHQueryDOSVar) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(RTR0Os2DHQueryDOSVar_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+BEGINCODE
+GLOBALNAME RTR0Os2DHQueryDOSVar_32
+ jc Error1
+
+ ;
+ ; Make ax:ebx contain the pointer and take action according
+ ; to the variable jump table.
+ ;
+ and ebx, 0000ffffh ; clean high part of ebx
+ movzx ecx, byte [ebp + 08] ; iVar
+ cmp ecx, DosVarJumpTabSize
+ jg Error2
+ jmp [DosVarJumpTab + ecx * 4]
+
+ ; Load Word at ax:ebx.
+LoadWord:
+ mov es, ax
+ movzx edx, word [es:ebx]
+ jmp StoreIt
+
+ ; Load selector at ax:ebx.
+Load1600:
+ mov es, ax
+ movzx edx, word [es:ebx]
+ shl edx, 16
+ jmp StoreIt
+
+ ; Load 16:16 ptr at ax:ebx.
+Load1616:
+ mov es, ax
+ mov edx, dword [es:ebx]
+ jmp StoreIt
+
+ ; Move ax:bx into edx.
+AsIs:
+ mov dx, ax
+ shl edx, 16
+ mov dx, bx
+ jmp StoreIt
+
+Error2:
+ mov eax, VERR_INVALID_PARAMETER
+ jmp Done
+
+Error1:
+ mov eax, VERR_GENERAL_FAILURE
+ jmp Done
+
+StoreIt:
+ mov ecx, [ebp + 10h]
+ mov [ecx], edx
+ xor eax, eax ; return success (VINF_SUCCESS == 0)
+
+Done:
+ pop es
+ pop ebx
+ leave
+
+ ; switch stack back and return.
+ push eax
+ call KernThunkStackTo32
+ pop eax
+ ret
+ENDPROC RTR0Os2DHQueryDOSVar
+
diff --git a/src/VBox/Runtime/r0drv/os2/RTR0Os2DHVMGlobalToProcess.asm b/src/VBox/Runtime/r0drv/os2/RTR0Os2DHVMGlobalToProcess.asm
new file mode 100644
index 00000000..a946080f
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/RTR0Os2DHVMGlobalToProcess.asm
@@ -0,0 +1,148 @@
+; $Id: RTR0Os2DHVMGlobalToProcess.asm $
+;; @file
+; IPRT - DevHelp_VMGlobalToProcess, Ring-0 Driver, OS/2.
+;
+
+;
+; Contributed by knut st. osmundsen.
+;
+; 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 code is based on:
+;
+; 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"
+%include "iprt/err.mac"
+
+
+;*******************************************************************************
+;* External Symbols *
+;*******************************************************************************
+extern KernThunkStackTo32
+extern KernThunkStackTo16
+extern NAME(g_fpfnDevHlp)
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+%define DevHlp_VMGlobalToProcess 05ah
+
+
+BEGINCODE
+
+;;
+; VMGlobalToProcess wrapper.
+;
+; @param fFlags [ebp + 08h] Flags
+; @param pvR0 [ebp + 0ch] Ring-0 memory.
+; @param cb [ebp + 10h] Size of memory object to map.
+; @param ppR3 [ebp + 14h] Where to store the address of the ring-3 mapping.
+;
+BEGINPROC_EXPORTED RTR0Os2DHVMGlobalToProcess
+ ; switch stack first.
+ call KernThunkStackTo16
+
+ ; normal prolog.
+ push ebp
+ mov ebp, esp
+ push dword [NAME(g_fpfnDevHlp)] ; ebp - 4
+ push ebx ; save ebx
+
+ ; setup the devhelp call
+ mov eax, [ebp + 08h] ; fFlags
+ mov ebx, [ebp + 0ch] ; pvR0
+ mov ecx, [ebp + 10h] ; cb
+ mov dl, DevHlp_VMGlobalToProcess
+
+ ; jump to the 16-bit code.
+ ;jmp far dword NAME(RTR0Os2DHQueryDOSVar_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(RTR0Os2DHVMGlobalToProcess_16) wrt CODE16
+ dw CODE16
+BEGINCODE16
+GLOBALNAME RTR0Os2DHVMGlobalToProcess_16
+ call far [ss:ebp - 4]
+
+ ;jmp far dword NAME(RTR0Os2DHVMGlobalToProcess_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(RTR0Os2DHVMGlobalToProcess_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+BEGINCODE
+GLOBALNAME RTR0Os2DHVMGlobalToProcess_32
+ jc .done
+
+ ; save the result.
+ mov edx, [ebp + 14h] ; ppvR3
+ mov [edx], eax
+ xor eax, eax
+
+.done:
+ pop ebx
+ leave
+
+ ; switch stack back and return.
+ push eax
+ call KernThunkStackTo32
+ pop eax
+ ret
+ENDPROC RTR0Os2DHVMGlobalToProcess
+
diff --git a/src/VBox/Runtime/r0drv/os2/alloc-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/alloc-r0drv-os2.cpp
new file mode 100644
index 00000000..c966649e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/alloc-r0drv-os2.cpp
@@ -0,0 +1,141 @@
+/* $Id: alloc-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/param.h>
+#include "r0drv/alloc-r0drv.h" /** @todo drop the r0drv/alloc-r0drv.cpp stuff on OS/2? */
+
+
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ if (fFlags & RTMEMHDR_FLAG_ANY_CTX)
+ return VERR_NOT_SUPPORTED;
+
+ void *pv = NULL;
+ APIRET rc = KernVMAlloc(cb + sizeof(RTMEMHDR), VMDHA_FIXED, &pv, (void **)-1, NULL);
+ if (RT_UNLIKELY(rc != NO_ERROR))
+ return RTErrConvertFromOS2(rc);
+
+ PRTMEMHDR pHdr = (PRTMEMHDR)pv;
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = cb;
+ pHdr->cbReq = cb;
+ *ppHdr = pHdr;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr)
+{
+ pHdr->u32Magic += 1;
+ APIRET rc = KernVMFree(pHdr);
+ Assert(!rc); NOREF(rc);
+}
+
+
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtr(pPhys);
+ Assert(cb > 0);
+
+ /*
+ * All physical memory in OS/2 is below 4GB, so this should be kind of easy.
+ */
+ cb = RT_ALIGN_Z(cb, PAGE_SIZE); /* -> page aligned result. */
+ PVOID pv = NULL;
+ PVOID PhysAddr = (PVOID)~0UL;
+ APIRET rc = KernVMAlloc(cb, VMDHA_FIXED | VMDHA_CONTIG, &pv, &PhysAddr, NULL);
+ if (!rc)
+ {
+ Assert(PhysAddr != (PVOID)~0UL);
+ Assert(!((uintptr_t)pv & PAGE_OFFSET_MASK));
+ *pPhys = (uintptr_t)PhysAddr;
+ return pv;
+ }
+ return NULL;
+}
+
+
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
+{
+ if (pv)
+ {
+ AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv));
+ KernVMFree(pv);
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/assert-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/assert-r0drv-os2.cpp
new file mode 100644
index 00000000..27ad1089
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/assert-r0drv-os2.cpp
@@ -0,0 +1,168 @@
+/* $Id: assert-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/stdarg.h>
+
+#include <VBox/log.h>
+
+#include "internal/assert.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN /* for watcom */
+/** The last assert message. (in DATA16) */
+extern char g_szRTAssertMsg[2048];
+/** The length of the last assert message. (in DATA16) */
+extern size_t g_cchRTAssertMsg;
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(size_t) rtR0Os2AssertOutputCB(void *pvArg, const char *pachChars, size_t cbChars);
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+#if defined(DEBUG_bird)
+ RTLogComPrintf("\n!!Assertion Failed!!\n"
+ "Expression: %s\n"
+ "Location : %s(%d) %s\n",
+ pszExpr, pszFile, uLine, pszFunction);
+#endif
+
+ g_cchRTAssertMsg = RTStrPrintf(g_szRTAssertMsg, sizeof(g_szRTAssertMsg),
+ "\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%d) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+#if defined(DEBUG_bird)
+ va_list vaCopy;
+ va_copy(vaCopy, va);
+ RTLogComPrintfV(pszFormat, vaCopy);
+ va_end(vaCopy);
+#endif
+
+ size_t cch = g_cchRTAssertMsg;
+ char *pch = &g_szRTAssertMsg[cch];
+ cch += RTStrFormatV(rtR0Os2AssertOutputCB, &pch, NULL, NULL, pszFormat, va);
+ g_cchRTAssertMsg = cch;
+
+ NOREF(fInitial);
+}
+
+
+/**
+ * Output callback.
+ *
+ * @returns number of bytes written.
+ * @param pvArg Pointer to a char pointer with the current output position.
+ * @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) rtR0Os2AssertOutputCB(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ char **ppch = (char **)pvArg;
+ char *pch = *ppch;
+
+ while (cbChars-- > 0)
+ {
+ const char ch = *pachChars++;
+ if (ch == '\r')
+ continue;
+ if (ch == '\n')
+ {
+ if (pch + 1 >= &g_szRTAssertMsg[sizeof(g_szRTAssertMsg)])
+ break;
+ *pch++ = '\r';
+ }
+ if (pch + 1 >= &g_szRTAssertMsg[sizeof(g_szRTAssertMsg)])
+ break;
+ *pch++ = ch;
+ }
+ *pch = '\0';
+
+ size_t cbWritten = pch - *ppch;
+ *ppch = pch;
+ return cbWritten;
+}
+
+
+/* RTR0AssertPanicSystem is implemented in RTR0AssertPanicSystem-r0drv-os2.asm */
+
diff --git a/src/VBox/Runtime/r0drv/os2/assertA-r0drv-os2.asm b/src/VBox/Runtime/r0drv/os2/assertA-r0drv-os2.asm
new file mode 100644
index 00000000..e3e34715
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/assertA-r0drv-os2.asm
@@ -0,0 +1,81 @@
+; $Id: assertA-r0drv-os2.asm $
+;; @file
+; IPRT - DevHelp_GetDOSVar, Ring-0 Driver, OS/2.
+;
+
+;
+; Contributed by knut st. osmundsen.
+;
+; 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 code is based on:
+;
+; Copyright (c) 1999-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"
+
+BEGINDATA16
+
+;; The last assert message. (see assert-r0drv-os2.cpp)
+EXPORTEDNAME g_szRTAssertMsg
+times 2048 db 0
+
+;; The length of the last assert message.
+EXPORTEDNAME g_cchRTAssertMsg
+ dd 0
+
diff --git a/src/VBox/Runtime/r0drv/os2/initterm-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/initterm-r0drv-os2.cpp
new file mode 100644
index 00000000..98821c29
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/initterm-r0drv-os2.cpp
@@ -0,0 +1,132 @@
+/* $Id: initterm-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Initialization & Termination, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include "internal/initterm.h"
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to the 1st DOS variable table. */
+PCDOSTABLE g_pDosTable = NULL;
+/** Pointer to the 2nd DOS variable table. */
+PCDOSTABLE2 g_pDosTable2 = NULL;
+/** Pointer to the global info segment. */
+PGINFOSEG g_pGIS = NULL;
+/** Far 16:16 pointer to the local info segment.
+ * IIRC this must be converted to a flat pointer when accessed to work correctly on SMP systems. */
+RTFAR16 g_fpLIS = {0, 0};
+
+
+DECLHIDDEN(int) rtR0InitNative(void)
+{
+ /*
+ * Get the DOS Tables.
+ */
+ RTFAR16 fp;
+ int rc = RTR0Os2DHQueryDOSVar(DHGETDOSV_DOSTABLES, 0, &fp);
+ AssertMsgReturn(!rc, ("rc=%d\n", rc), VERR_INTERNAL_ERROR);
+ g_pDosTable = (PCDOSTABLE)RTR0Os2Virt2Flat(fp);
+ AssertReturn(g_pDosTable, VERR_INTERNAL_ERROR);
+ g_pDosTable2 = (PCDOSTABLE2)((const uint8_t *)g_pDosTable + g_pDosTable->cul * sizeof(ULONG) + 1);
+
+ /*
+ * Get the addresses of the two info segments.
+ */
+ rc = RTR0Os2DHQueryDOSVar(DHGETDOSV_SYSINFOSEG, 0, &fp);
+ AssertMsgReturn(!rc, ("rc=%d\n", rc), VERR_INTERNAL_ERROR);
+ g_pGIS = (PGINFOSEG)RTR0Os2Virt2Flat(fp);
+ rc = RTR0Os2DHQueryDOSVar(DHGETDOSV_LOCINFOSEG, 0, &fp);
+ AssertMsgReturn(!rc, ("rc=%d\n", rc), VERR_INTERNAL_ERROR);
+ g_fpLIS = fp;
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0TermNative(void)
+{
+ /* nothing to do here yet. */
+}
+
+
+/**
+ * Converts a 16:16 pointer to a flat pointer.
+ *
+ * @returns Flat pointer (NULL if fp is NULL).
+ * @param fp Far pointer to convert.
+ */
+RTR0DECL(void *) RTR0Os2Virt2Flat(RTFAR16 fp)
+{
+ return (void *)KernSelToFlat(((uint32_t)fp.sel << 16) | fp.off);
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/memobj-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/memobj-r0drv-os2.cpp
new file mode 100644
index 00000000..b79a0424
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/memobj-r0drv-os2.cpp
@@ -0,0 +1,633 @@
+/* $Id: memobj-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include <iprt/memobj.h>
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/assert.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include "internal/memobj.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The OS/2 version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJDARWIN
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ /** Lock for the ring-3 / ring-0 pinned objectes.
+ * This member might not be allocated for some object types. */
+ KernVMLock_t Lock;
+ /** Array of physical pages.
+ * This array can be 0 in length for some object types. */
+ KernPageList_t aPages[1];
+} RTR0MEMOBJOS2, *PRTR0MEMOBJOS2;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtR0MemObjFixPageList(KernPageList_t *paPages, ULONG cPages, ULONG cPagesRet);
+
+
+DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)pMem;
+ int rc;
+
+ switch (pMemOs2->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ AssertMsgFailed(("RTR0MEMOBJTYPE_PHYS_NC\n"));
+ return VERR_INTERNAL_ERROR;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ if (!pMemOs2->Core.pv)
+ break;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ if (pMemOs2->Core.u.Mapping.R0Process == NIL_RTR0PROCESS)
+ break;
+
+ RT_FALL_THRU();
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ rc = KernVMFree(pMemOs2->Core.pv);
+ AssertMsg(!rc, ("rc=%d type=%d pv=%p cb=%#zx\n", rc, pMemOs2->Core.enmType, pMemOs2->Core.pv, pMemOs2->Core.cb));
+ break;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ rc = KernVMUnlock(&pMemOs2->Lock);
+ AssertMsg(!rc, ("rc=%d\n", rc));
+ break;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemOs2->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ NOREF(fExecutable);
+
+ /* create the object. */
+ const ULONG cPages = cb >> PAGE_SHIFT;
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJOS2, aPages[cPages]),
+ RTR0MEMOBJTYPE_PAGE, NULL, cb, pszTag);
+ if (pMemOs2)
+ {
+ /* do the allocation. */
+ int rc = KernVMAlloc(cb, VMDHA_FIXED, &pMemOs2->Core.pv, (PPVOID)-1, NULL);
+ if (!rc)
+ {
+ ULONG cPagesRet = cPages;
+ rc = KernLinToPageList(pMemOs2->Core.pv, cb, &pMemOs2->aPages[0], &cPagesRet);
+ if (!rc)
+ {
+ rtR0MemObjFixPageList(&pMemOs2->aPages[0], cPages, cPagesRet);
+ pMemOs2->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /* doesn't seem to be possible to zero anything */
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ KernVMFree(pMemOs2->Core.pv);
+ }
+ rtR0MemObjDelete(&pMemOs2->Core);
+ return RTErrConvertFromOS2(rc);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ NOREF(fExecutable);
+
+ /* create the object. */
+ const ULONG cPages = cb >> PAGE_SHIFT;
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJOS2, aPages[cPages]),
+ RTR0MEMOBJTYPE_LOW, NULL, cb, pszTag);
+ if (pMemOs2)
+ {
+ /* do the allocation. */
+ int rc = KernVMAlloc(cb, VMDHA_FIXED, &pMemOs2->Core.pv, (PPVOID)-1, NULL);
+ if (!rc)
+ {
+ ULONG cPagesRet = cPages;
+ rc = KernLinToPageList(pMemOs2->Core.pv, cb, &pMemOs2->aPages[0], &cPagesRet);
+ if (!rc)
+ {
+ rtR0MemObjFixPageList(&pMemOs2->aPages[0], cPages, cPagesRet);
+ pMemOs2->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /* doesn't seem to be possible to zero anything */
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ KernVMFree(pMemOs2->Core.pv);
+ }
+ rtR0MemObjDelete(&pMemOs2->Core);
+ rc = RTErrConvertFromOS2(rc);
+ return rc == VERR_NO_MEMORY ? VERR_NO_LOW_MEMORY : rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ NOREF(fExecutable);
+
+ /* create the object. */
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF(RTR0MEMOBJOS2, Lock), RTR0MEMOBJTYPE_CONT,
+ NULL, cb, pszTag);
+ if (pMemOs2)
+ {
+ /* do the allocation. */
+ ULONG ulPhys = ~0UL;
+ int rc = KernVMAlloc(cb, VMDHA_FIXED | VMDHA_CONTIG, &pMemOs2->Core.pv, (PPVOID)&ulPhys, NULL);
+ if (!rc)
+ {
+ Assert(ulPhys != ~0UL);
+ pMemOs2->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /* doesn't seem to be possible to zero anything */
+ pMemOs2->Core.u.Cont.Phys = ulPhys;
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemOs2->Core);
+ return RTErrConvertFromOS2(rc);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment,
+ const char *pszTag)
+{
+ AssertMsgReturn(PhysHighest >= 16 *_1M, ("PhysHigest=%RHp\n", PhysHighest), VERR_NOT_SUPPORTED);
+
+ /** @todo alignment */
+ if (uAlignment != PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /* create the object. */
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF(RTR0MEMOBJOS2, Lock), RTR0MEMOBJTYPE_PHYS,
+ NULL, cb, pszTag);
+ if (pMemOs2)
+ {
+ /* do the allocation. */
+ ULONG ulPhys = ~0UL;
+ int rc = KernVMAlloc(cb, VMDHA_FIXED | VMDHA_CONTIG | (PhysHighest < _4G ? VMDHA_16M : 0),
+ &pMemOs2->Core.pv, (PPVOID)&ulPhys, NULL);
+ if (!rc)
+ {
+ Assert(ulPhys != ~0UL);
+ pMemOs2->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /* doesn't seem to be possible to zero anything */
+ pMemOs2->Core.u.Phys.fAllocated = true;
+ pMemOs2->Core.u.Phys.PhysBase = ulPhys;
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemOs2->Core);
+ return RTErrConvertFromOS2(rc);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+ /** @todo rtR0MemObjNativeAllocPhysNC / os2. */
+ return rtR0MemObjNativeAllocPhys(ppMem, cb, PhysHighest, PAGE_SIZE, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy,
+ const char *pszTag)
+{
+ AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE, VERR_NOT_SUPPORTED);
+
+ /* create the object. */
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF(RTR0MEMOBJOS2, Lock), RTR0MEMOBJTYPE_PHYS,
+ NULL, cb, pszTag);
+ if (pMemOs2)
+ {
+ /* there is no allocation here, right? it needs to be mapped somewhere first. */
+ pMemOs2->Core.u.Phys.fAllocated = false;
+ pMemOs2->Core.u.Phys.PhysBase = Phys;
+ pMemOs2->Core.u.Phys.uCachePolicy = uCachePolicy;
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED);
+
+ /* create the object. */
+ const ULONG cPages = cb >> PAGE_SHIFT;
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJOS2, aPages[cPages]),
+ RTR0MEMOBJTYPE_LOCK, (void *)R3Ptr, cb, pszTag);
+ if (pMemOs2)
+ {
+ /* lock it. */
+ ULONG cPagesRet = cPages;
+ int rc = KernVMLock(VMDHL_LONG | (fAccess & RTMEM_PROT_WRITE ? VMDHL_WRITE : 0),
+ (void *)R3Ptr, cb, &pMemOs2->Lock, &pMemOs2->aPages[0], &cPagesRet);
+ if (!rc)
+ {
+ rtR0MemObjFixPageList(&pMemOs2->aPages[0], cPages, cPagesRet);
+ Assert(cb == pMemOs2->Core.cb);
+ Assert(R3Ptr == (RTR3PTR)pMemOs2->Core.pv);
+ pMemOs2->Core.u.Lock.R0Process = R0Process;
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemOs2->Core);
+ return RTErrConvertFromOS2(rc);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ /* create the object. */
+ const ULONG cPages = cb >> PAGE_SHIFT;
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF_DYN(RTR0MEMOBJOS2, aPages[cPages]),
+ RTR0MEMOBJTYPE_LOCK, pv, cb, pszTag);
+ if (pMemOs2)
+ {
+ /* lock it. */
+ ULONG cPagesRet = cPages;
+ int rc = KernVMLock(VMDHL_LONG | (fAccess & RTMEM_PROT_WRITE ? VMDHL_WRITE : 0),
+ pv, cb, &pMemOs2->Lock, &pMemOs2->aPages[0], &cPagesRet);
+ if (!rc)
+ {
+ rtR0MemObjFixPageList(&pMemOs2->aPages[0], cPages, cPagesRet);
+ pMemOs2->Core.u.Lock.R0Process = NIL_RTR0PROCESS;
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemOs2->Core);
+ return RTErrConvertFromOS2(rc);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ const char *pszTag)
+{
+ RT_NOREF(ppMem, pvFixed, cb, uAlignment, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ RT_NOREF(ppMem, R3PtrFixed, cb, uAlignment, R0Process, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ AssertMsgReturn(pvFixed == (void *)-1, ("%p\n", pvFixed), VERR_NOT_SUPPORTED);
+
+ /*
+ * Check that the specified alignment is supported.
+ */
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+/** @todo finish the implementation. */
+
+ int rc;
+ void *pvR0 = NULL;
+ PRTR0MEMOBJOS2 pMemToMapOs2 = (PRTR0MEMOBJOS2)pMemToMap;
+ switch (pMemToMapOs2->Core.enmType)
+ {
+ /*
+ * These has kernel mappings.
+ */
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ pvR0 = pMemToMapOs2->Core.pv;
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ pvR0 = pMemToMapOs2->Core.pv;
+ if (!pvR0)
+ {
+ /* no ring-0 mapping, so allocate a mapping in the process. */
+ AssertMsgReturn(fProt & RTMEM_PROT_WRITE, ("%#x\n", fProt), VERR_NOT_SUPPORTED);
+ Assert(!pMemToMapOs2->Core.u.Phys.fAllocated);
+ ULONG ulPhys = (ULONG)pMemToMapOs2->Core.u.Phys.PhysBase;
+ AssertReturn(ulPhys == pMemToMapOs2->Core.u.Phys.PhysBase, VERR_OUT_OF_RANGE);
+ rc = KernVMAlloc(pMemToMapOs2->Core.cb, VMDHA_PHYS, &pvR0, (PPVOID)&ulPhys, NULL);
+ if (rc)
+ return RTErrConvertFromOS2(rc);
+ pMemToMapOs2->Core.pv = pvR0;
+ }
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ AssertMsgFailed(("RTR0MEMOBJTYPE_PHYS_NC\n"));
+ return VERR_INTERNAL_ERROR_3;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ if (pMemToMapOs2->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ return VERR_NOT_SUPPORTED; /** @todo implement this... */
+ pvR0 = pMemToMapOs2->Core.pv;
+ break;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ case RTR0MEMOBJTYPE_MAPPING:
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemToMapOs2->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Create a dummy mapping object for it.
+ *
+ * All mappings are read/write/execute in OS/2 and there isn't
+ * any cache options, so sharing is ok. And the main memory object
+ * isn't actually freed until all the mappings have been freed up
+ * (reference counting).
+ */
+ if (!cbSub)
+ cbSub = pMemToMapOs2->Core.cb - offSub;
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF(RTR0MEMOBJOS2, Lock), RTR0MEMOBJTYPE_MAPPING,
+ (uint8_t *)pvR0 + offSub, cbSub, pszTag);
+ if (pMemOs2)
+ {
+ pMemOs2->Core.u.Mapping.R0Process = NIL_RTR0PROCESS;
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment,
+ unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED);
+ AssertMsgReturn(R3PtrFixed == (RTR3PTR)-1, ("%p\n", R3PtrFixed), VERR_NOT_SUPPORTED);
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+ AssertMsgReturn(!offSub && !cbSub, ("%#zx %#zx\n", offSub, cbSub), VERR_NOT_SUPPORTED); /** @todo implement sub maps */
+
+ int rc;
+ void *pvR0;
+ void *pvR3 = NULL;
+ PRTR0MEMOBJOS2 pMemToMapOs2 = (PRTR0MEMOBJOS2)pMemToMap;
+ switch (pMemToMapOs2->Core.enmType)
+ {
+ /*
+ * These has kernel mappings.
+ */
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_CONT:
+ pvR0 = pMemToMapOs2->Core.pv;
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ pvR0 = pMemToMapOs2->Core.pv;
+#if 0/* this is wrong. */
+ if (!pvR0)
+ {
+ /* no ring-0 mapping, so allocate a mapping in the process. */
+ AssertMsgReturn(fProt & RTMEM_PROT_WRITE, ("%#x\n", fProt), VERR_NOT_SUPPORTED);
+ Assert(!pMemToMapOs2->Core.u.Phys.fAllocated);
+ ULONG ulPhys = pMemToMapOs2->Core.u.Phys.PhysBase;
+ rc = KernVMAlloc(pMemToMapOs2->Core.cb, VMDHA_PHYS | VMDHA_PROCESS, &pvR3, (PPVOID)&ulPhys, NULL);
+ if (rc)
+ return RTErrConvertFromOS2(rc);
+ }
+ break;
+#endif
+ return VERR_NOT_SUPPORTED;
+
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ AssertMsgFailed(("RTR0MEMOBJTYPE_PHYS_NC\n"));
+ return VERR_INTERNAL_ERROR_5;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ if (pMemToMapOs2->Core.u.Lock.R0Process != NIL_RTR0PROCESS)
+ return VERR_NOT_SUPPORTED; /** @todo implement this... */
+ pvR0 = pMemToMapOs2->Core.pv;
+ break;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ case RTR0MEMOBJTYPE_MAPPING:
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemToMapOs2->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Map the ring-0 memory into the current process.
+ */
+ if (!pvR3)
+ {
+ Assert(pvR0);
+ ULONG flFlags = 0;
+ if (uAlignment == PAGE_SIZE)
+ flFlags |= VMDHGP_4MB;
+ if (fProt & RTMEM_PROT_WRITE)
+ flFlags |= VMDHGP_WRITE;
+ rc = RTR0Os2DHVMGlobalToProcess(flFlags, pvR0, pMemToMapOs2->Core.cb, &pvR3);
+ if (rc)
+ return RTErrConvertFromOS2(rc);
+ }
+ Assert(pvR3);
+
+ /*
+ * Create a mapping object for it.
+ */
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)rtR0MemObjNew(RT_UOFFSETOF(RTR0MEMOBJOS2, Lock), RTR0MEMOBJTYPE_MAPPING,
+ pvR3, pMemToMapOs2->Core.cb, pszTag);
+ if (pMemOs2)
+ {
+ Assert(pMemOs2->Core.pv == pvR3);
+ pMemOs2->Core.u.Mapping.R0Process = R0Process;
+ *ppMem = &pMemOs2->Core;
+ return VINF_SUCCESS;
+ }
+ KernVMFree(pvR3);
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ NOREF(pMem);
+ NOREF(offSub);
+ NOREF(cbSub);
+ NOREF(fProt);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ PRTR0MEMOBJOS2 pMemOs2 = (PRTR0MEMOBJOS2)pMem;
+
+ switch (pMemOs2->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_LOCK:
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ return pMemOs2->aPages[iPage].Addr;
+
+ case RTR0MEMOBJTYPE_CONT:
+ return pMemOs2->Core.u.Cont.Phys + (iPage << PAGE_SHIFT);
+
+ case RTR0MEMOBJTYPE_PHYS:
+ return pMemOs2->Core.u.Phys.PhysBase + (iPage << PAGE_SHIFT);
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ case RTR0MEMOBJTYPE_MAPPING:
+ default:
+ return NIL_RTHCPHYS;
+ }
+}
+
+
+/**
+ * Expands the page list so we can index pages directly.
+ *
+ * @param paPages The page list array to fix.
+ * @param cPages The number of pages that's supposed to go into the list.
+ * @param cPagesRet The actual number of pages in the list.
+ */
+static void rtR0MemObjFixPageList(KernPageList_t *paPages, ULONG cPages, ULONG cPagesRet)
+{
+ Assert(cPages >= cPagesRet);
+ if (cPages != cPagesRet)
+ {
+ ULONG iIn = cPagesRet;
+ ULONG iOut = cPages;
+ do
+ {
+ iIn--;
+ iOut--;
+ Assert(iIn <= iOut);
+
+ KernPageList_t Page = paPages[iIn];
+ Assert(!(Page.Addr & PAGE_OFFSET_MASK));
+ Assert(Page.Size == RT_ALIGN_Z(Page.Size, PAGE_SIZE));
+
+ if (Page.Size > PAGE_SIZE)
+ {
+ do
+ {
+ Page.Size -= PAGE_SIZE;
+ paPages[iOut].Addr = Page.Addr + Page.Size;
+ paPages[iOut].Size = PAGE_SIZE;
+ iOut--;
+ } while (Page.Size > PAGE_SIZE);
+ }
+
+ paPages[iOut].Addr = Page.Addr;
+ paPages[iOut].Size = PAGE_SIZE;
+ } while ( iIn != iOut
+ && iIn > 0);
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/memuserkernel-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/memuserkernel-r0drv-os2.cpp
new file mode 100644
index 00000000..eb4825a3
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/memuserkernel-r0drv-os2.cpp
@@ -0,0 +1,101 @@
+/* $Id: memuserkernel-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - User & Kernel Memory, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * 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-os2-kernel.h"
+
+#include <iprt/mem.h>
+#include <iprt/errcore.h>
+
+
+RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb)
+{
+ int rc = KernCopyIn(pvDst, (void *)R3PtrSrc, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb)
+{
+ int rc = KernCopyOut((void *)R3PtrDst, (void *)pvSrc, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr)
+{
+ /** @todo this is all wrong, but I'm too lazy to figure out how to make it
+ * correct. Checking the user DS limit would work if it wasn't maxed
+ * out by SDD, VPC or someone. The version (+SMP) would help on older
+ * OS/2 versions where the limit is 512MB. */
+ return R3Ptr < UINT32_C(0xc0000000); /* 3GB */
+}
+
+
+RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv)
+{
+ /** @todo this is all wrong, see RTR0MemUserIsValidAddr. */
+ return (uintptr_t)pv >= UINT32_C(0x20000000); /* 512MB */
+}
+
+
+RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void)
+{
+ /** @todo this is all wrong, see RTR0MemUserIsValidAddr. */
+ return false;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb)
+{
+ RT_NOREF(pvDst, pvSrc, cb);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb)
+{
+ RT_NOREF(pvDst, pvSrc, cb);
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/os2imports.imp b/src/VBox/Runtime/r0drv/os2/os2imports.imp
new file mode 100644
index 00000000..26feb406
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/os2imports.imp
@@ -0,0 +1,121 @@
+;
+; DOSCALLS
+;
+DOS16OPEN DOSCALLS 70 ?
+DOS16CLOSE DOSCALLS 59 ?
+DOS16READ DOSCALLS 137 ?
+DOS16WRITE DOSCALL1 138 ?
+DOS16IREAD DOSCALL1 79 ?
+DOS16IWRITE DOSCALL1 87 ?
+DOS16DEVIOCTL DOSCALL1 53 ?
+DOS16DEVIOCTL2 DOSCALL1 99 ?
+DOS16FSATTACH DOSCALL1 181 ?
+DOS16FSCTL DOSCALL1 183 ?
+
+;
+; KEE
+;
+KernAllocSpinLock KEE 10 ?
+KernFreeSpinLock KEE 11 ?
+KernAcquireSpinLock KEE 12 ?
+KernReleaseSpinLock KEE 13 ?
+KernAllocMutexLock KEE 20 ?
+KernFreeMutexLock KEE 21 ?
+KernRequestSharedMutex KEE 22 ?
+KernReleaseSharedMutex KEE 23 ?
+KernTryRequestSharedMutex KEE 24 ?
+KernRequestExclusiveMutex KEE 25 ?
+KernReleaseExclusiveMutex KEE 26 ?
+KernTryRequestExclusiveMutex KEE 27 ?
+KernBlock KEE 30 ?
+KernWakeup KEE 31 ?
+KernThunkStackTo16 KEE 40 ?
+KernThunkStackTo32 KEE 41 ?
+KernSerialize16BitDD KEE 42 ?
+KernUnserialize16BitDD KEE 43 ?
+KernArmHook KEE 44 ?
+KernAllocateContextHook KEE 45 ?
+KernCopyIn KEE 50 ?
+KernCopyOut KEE 51 ?
+KernVMAlloc KEE 60 ?
+KernVMFree KEE 61 ?
+KernVMLock KEE 62 ?
+KernVMUnlock KEE 63 ?
+KernLinToPageList KEE 64 ?
+KernVMSetMem KEE 65 ?
+KernSelToFlat KEE 66 ?
+KernDynamicAPI KEE 70 ?
+KernRASSysTrace KEE 80 ?
+KernPerfSysTrace KEE 81 ?
+_KernSISData KEE 90 ?
+_KernLISData KEE 91 ?
+_KernInterruptLevel KEE 92 ?
+_KernTKSSBase KEE 93 ?
+_KernKEEVersion KEE 94 ?
+KernLockFile KEE 100 ?
+KernUnLockFile KEE 101 ?
+KernGetFileSize KEE 102 ?
+KernTestFileCache KEE 103 ?
+KernReadFileAt KEE 104 ?
+KernReadFileAtCache KEE 105 ?
+KernReturnFileCache KEE 106 ?
+KernCreateUconvObject KEE 120 ?
+KernStrFromUcs KEE 121 ?
+KernStrToUcs KEE 122 ?
+
+FSH_SEGALLOC FSHELPER 1 ?
+FSH_SEGFREE FSHELPER 2 ?
+FSH_SEGREALLOC FSHELPER 3 ?
+FSH_FORCENOSWAP FSHELPER 4 ?
+FSH_INTERR FSHELPER 5 ?
+FSH_SEMREQUEST FSHELPER 6 ?
+FSH_SEMCLEAR FSHELPER 7 ?
+FSH_PROBEBUF FSHELPER 8 ?
+FSH_GETPRIORITY FSHELPER 9 ?
+FSH_IOSEMCLEAR FSHELPER 10 ?
+FSH_FLUSHBUF FSHELPER 11 ?
+FSH_CRITERROR FSHELPER 12 ?
+FSH_DEVIOCTL FSHELPER 13 ?
+FSH_GETVOLPARM FSHELPER 14 ?
+FSH_FINDDUPHVPB FSHELPER 15 ?
+FSH_DOVOLIO FSHELPER 16 ?
+FSH_ADDSHARE FSHELPER 17 ?
+FSH_REMOVESHARE FSHELPER 18 ?
+FSH_GETOVERLAPBUF FSHELPER 19 ?
+FSH_ISCURDIRPREFIX FSHELPER 20 ?
+FSH_LOADCHAR FSHELPER 21 ?
+FSH_PREVCHAR FSHELPER 22 ?
+FSH_STORECHAR FSHELPER 23 ?
+FSH_SEMSET FSHELPER 24 ?
+FSH_SEMSETWAIT FSHELPER 25 ?
+FSH_SEMWAIT FSHELPER 26 ?
+FSH_WILDMATCH FSHELPER 27 ?
+FSH_YIELD FSHELPER 28 ?
+FSH_DOVOLIO2 FSHELPER 29 ?
+FSH_FINDCHAR FSHELPER 30 ?
+MFSH_SETBOOTDRIVE FSHELPER 31 ?
+FSH_CANONICALIZE FSHELPER 32 ?
+MFSH_DOVOLIO FSHELPER 33 ?
+MFSH_INTERR FSHELPER 34 ?
+MFSH_SEGALLOC FSHELPER 35 ?
+MFSH_SEGFREE FSHELPER 36 ?
+MFSH_SEGREALLOC FSHELPER 37 ?
+MFSH_CALLRM FSHELPER 38 ?
+MFSH_LOCK FSHELPER 39 ?
+MFSH_PHYSTOVIRT FSHELPER 40 ?
+MFSH_UNLOCK FSHELPER 41 ?
+MFSH_UNPHYSTOVIRT FSHELPER 42 ?
+MFSH_VIRT2PHYS FSHELPER 43 ?
+FSH_QSYSINFO FSHELPER 44 ?
+FSH_NAMEFROMSFN FSHELPER 45 ?
+FSH_UPPERCASE FSHELPER 46 ?
+FSH_CHECKEANAME FSHELPER 47 ?
+FSH_CALLDRIVER FSHELPER 48 ?
+FSH_SETVOLUME FSHELPER 49 ?
+FSH_STACKSPACE FSHELPER 50 ?
+FSH_REGISTERPERFCTRS FSHELPER 51 ?
+FSH_IOBOOST FSHELPER 52 ?
+FSH_QUERYSERVERTHREAD FSHELPER 53 ?
+FSH_QUERYOPLOCK FSHELPER 54 ?
+FSH_EXTENDTIMESLICE FSHELPER 55 ?
+
diff --git a/src/VBox/Runtime/r0drv/os2/process-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/process-r0drv-os2.cpp
new file mode 100644
index 00000000..e128c0c6
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/process-r0drv-os2.cpp
@@ -0,0 +1,88 @@
+/* $Id: process-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Process Management, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include <iprt/process.h>
+#include <iprt/assert.h>
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ PLINFOSEG pLIS = (PLINFOSEG)RTR0Os2Virt2Flat(g_fpLIS);
+ AssertReturn(pLIS, NIL_RTPROCESS);
+ return pLIS->pidCurrent;
+}
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ /* make this ptda later... */
+ return (RTR0PROCESS)RTProcSelf();
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/semevent-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/semevent-r0drv-os2.cpp
new file mode 100644
index 00000000..22825633
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/semevent-r0drv-os2.cpp
@@ -0,0 +1,311 @@
+/* $Id: semevent-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Single Release Event Semaphores, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/lockvalidator.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * OS/2 event semaphore.
+ */
+typedef struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The number of waiting threads. */
+ uint32_t volatile cWaiters;
+ /** Set if the event object is signaled. */
+ uint8_t volatile fSignaled;
+ /** The number of threads in the process of waking up. */
+ uint32_t volatile cWaking;
+ /** The OS/2 spinlock protecting this structure. */
+ SpinLock_t Spinlock;
+} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+ AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
+ RT_NOREF(hClass, pszNameFmt);
+
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->cWaiters = 0;
+ pThis->cWaking = 0;
+ pThis->fSignaled = 0;
+ KernAllocSpinLock(&pThis->Spinlock);
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ PRTSEMEVENTINTERNAL pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+ KernAcquireSpinLock(&pThis->Spinlock);
+ ASMAtomicIncU32(&pThis->u32Magic); /* make the handle invalid */
+ if (pThis->cWaiters > 0)
+ {
+ /* abort waiting thread, last man cleans up. */
+ ASMAtomicXchgU32(&pThis->cWaking, pThis->cWaking + pThis->cWaiters);
+ ULONG cThreads;
+ KernWakeup((ULONG)pThis, WAKEUP_DATA | WAKEUP_BOOST, &cThreads, (ULONG)VERR_SEM_DESTROYED);
+ KernReleaseSpinLock(&pThis->Spinlock);
+ }
+ else if (pThis->cWaking)
+ /* the last waking thread is gonna do the cleanup */
+ KernReleaseSpinLock(&pThis->Spinlock);
+ else
+ {
+ KernReleaseSpinLock(&pThis->Spinlock);
+ KernFreeSpinLock(&pThis->Spinlock);
+ RTMemFree(pThis);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+ KernAcquireSpinLock(&pThis->Spinlock);
+
+ if (pThis->cWaiters > 0)
+ {
+ ASMAtomicDecU32(&pThis->cWaiters);
+ ASMAtomicIncU32(&pThis->cWaking);
+ ULONG cThreads;
+ KernWakeup((ULONG)pThis, WAKEUP_DATA | WAKEUP_ONE, &cThreads, VINF_SUCCESS);
+ if (RT_UNLIKELY(!cThreads))
+ {
+ /* shouldn't ever happen on OS/2 */
+ ASMAtomicXchgU8(&pThis->fSignaled, true);
+ ASMAtomicDecU32(&pThis->cWaking);
+ ASMAtomicIncU32(&pThis->cWaiters);
+ }
+ }
+ else
+ ASMAtomicXchgU8(&pThis->fSignaled, true);
+
+ KernReleaseSpinLock(&pThis->Spinlock);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventOs2Wait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate and convert input.
+ */
+ if (!pThis)
+ return VERR_INVALID_HANDLE;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ ULONG cMsTimeout = rtR0SemWaitOs2ConvertTimeout(fFlags, uTimeout);
+ ULONG fBlock = BLOCK_SPINLOCK;
+ if (!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE))
+ fBlock |= BLOCK_UNINTERRUPTABLE;
+
+ /*
+ * Do the job.
+ */
+ KernAcquireSpinLock(&pThis->Spinlock);
+
+ int rc;
+ if (pThis->fSignaled)
+ {
+ Assert(!pThis->cWaiters);
+ ASMAtomicXchgU8(&pThis->fSignaled, false);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ ASMAtomicIncU32(&pThis->cWaiters);
+
+ ULONG ulData = (ULONG)VERR_INTERNAL_ERROR;
+ rc = KernBlock((ULONG)pThis, cMsTimeout, fBlock,
+ &pThis->Spinlock,
+ &ulData);
+ switch (rc)
+ {
+ case NO_ERROR:
+ rc = (int)ulData;
+ Assert(rc == VINF_SUCCESS || rc == VERR_SEM_DESTROYED);
+ Assert(pThis->cWaking > 0);
+ if ( !ASMAtomicDecU32(&pThis->cWaking)
+ && pThis->u32Magic != RTSEMEVENT_MAGIC)
+ {
+ /* The event was destroyed (ulData == VINF_SUCCESS if it was after we awoke), as
+ the last thread do the cleanup. */
+ KernReleaseSpinLock(&pThis->Spinlock);
+ KernFreeSpinLock(&pThis->Spinlock);
+ RTMemFree(pThis);
+ return rc;
+ }
+ break;
+
+ case ERROR_TIMEOUT:
+ Assert(cMsTimeout != SEM_INDEFINITE_WAIT);
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = VERR_TIMEOUT;
+ break;
+
+ case ERROR_INTERRUPT:
+ Assert(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = VERR_INTERRUPTED;
+ break;
+
+ default:
+ AssertMsgFailed(("rc=%d\n", rc));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+ }
+
+ KernReleaseSpinLock(&pThis->Spinlock);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventOs2Wait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventOs2Wait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventOs2Wait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ return 32000000; /* 32ms */
+}
+
+
+RTR0DECL(bool) RTSemEventIsSignalSafe(void)
+{
+ return true;
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/semeventmulti-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/semeventmulti-r0drv-os2.cpp
new file mode 100644
index 00000000..e8404929
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/semeventmulti-r0drv-os2.cpp
@@ -0,0 +1,321 @@
+/* $Id: semeventmulti-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * OS/2 multiple release event semaphore.
+ */
+typedef struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The number of waiting threads. */
+ uint32_t volatile cWaiters;
+ /** Set if the event object is signaled. */
+ uint8_t volatile fSignaled;
+ /** The number of threads in the process of waking up. */
+ uint32_t volatile cWaking;
+ /** The OS/2 spinlock protecting this structure. */
+ SpinLock_t Spinlock;
+} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phEventMultiSem, VERR_INVALID_POINTER);
+
+ AssertCompile(sizeof(RTSEMEVENTMULTIINTERNAL) > sizeof(void *));
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->cWaiters = 0;
+ pThis->cWaking = 0;
+ pThis->fSignaled = 0;
+ KernAllocSpinLock(&pThis->Spinlock);
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+ RT_NOREF(hClass, pszNameFmt);
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ KernAcquireSpinLock(&pThis->Spinlock);
+ ASMAtomicIncU32(&pThis->u32Magic); /* make the handle invalid */
+ if (pThis->cWaiters > 0)
+ {
+ /* abort waiting thread, last man cleans up. */
+ ASMAtomicXchgU32(&pThis->cWaking, pThis->cWaking + pThis->cWaiters);
+ ULONG cThreads;
+ KernWakeup((ULONG)pThis, WAKEUP_DATA | WAKEUP_BOOST, &cThreads, (ULONG)VERR_SEM_DESTROYED);
+ KernReleaseSpinLock(&pThis->Spinlock);
+ }
+ else if (pThis->cWaking)
+ /* the last waking thread is gonna do the cleanup */
+ KernReleaseSpinLock(&pThis->Spinlock);
+ else
+ {
+ KernReleaseSpinLock(&pThis->Spinlock);
+ KernFreeSpinLock(&pThis->Spinlock);
+ RTMemFree(pThis);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+
+ KernAcquireSpinLock(&pThis->Spinlock);
+
+ ASMAtomicXchgU8(&pThis->fSignaled, true);
+ if (pThis->cWaiters > 0)
+ {
+ ASMAtomicXchgU32(&pThis->cWaking, pThis->cWaking + pThis->cWaiters);
+ ASMAtomicXchgU32(&pThis->cWaiters, 0);
+ ULONG cThreads;
+ KernWakeup((ULONG)pThis, WAKEUP_DATA, &cThreads, VINF_SUCCESS);
+ }
+
+ KernReleaseSpinLock(&pThis->Spinlock);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+
+ KernAcquireSpinLock(&pThis->Spinlock);
+ ASMAtomicXchgU8(&pThis->fSignaled, false);
+ KernReleaseSpinLock(&pThis->Spinlock);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventMultiOs2Wait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ RT_NOREF(pSrcPos);
+
+ /*
+ * Validate and convert the input.
+ */
+ if (!pThis)
+ return VERR_INVALID_HANDLE;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ ULONG cMsTimeout = rtR0SemWaitOs2ConvertTimeout(fFlags, uTimeout);
+ ULONG fBlock = BLOCK_SPINLOCK;
+ if (!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE))
+ fBlock |= BLOCK_UNINTERRUPTABLE;
+
+ /*
+ * Do the job.
+ */
+ KernAcquireSpinLock(&pThis->Spinlock);
+
+ int rc;
+ if (pThis->fSignaled)
+ rc = VINF_SUCCESS;
+ else
+ {
+ ASMAtomicIncU32(&pThis->cWaiters);
+
+ ULONG ulData = (ULONG)VERR_INTERNAL_ERROR;
+ rc = KernBlock((ULONG)pThis, cMsTimeout, fBlock,
+ &pThis->Spinlock,
+ &ulData);
+ switch (rc)
+ {
+ case NO_ERROR:
+ rc = (int)ulData;
+ Assert(rc == VINF_SUCCESS || rc == VERR_SEM_DESTROYED);
+ Assert(pThis->cWaking > 0);
+ if ( !ASMAtomicDecU32(&pThis->cWaking)
+ && pThis->u32Magic != RTSEMEVENTMULTI_MAGIC)
+ {
+ /* The event was destroyed (ulData == VINF_SUCCESS if it was after we awoke), as
+ the last thread do the cleanup. */
+ KernReleaseSpinLock(&pThis->Spinlock);
+ KernFreeSpinLock(&pThis->Spinlock);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+ }
+ rc = VINF_SUCCESS;
+ break;
+
+ case ERROR_TIMEOUT:
+ Assert(cMsTimeout != SEM_INDEFINITE_WAIT);
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = VERR_TIMEOUT;
+ break;
+
+ case ERROR_INTERRUPT:
+ Assert(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
+ ASMAtomicDecU32(&pThis->cWaiters);
+ rc = VERR_INTERRUPTED;
+ break;
+
+ default:
+ AssertMsgFailed(("rc=%d\n", rc));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+ }
+
+ KernReleaseSpinLock(&pThis->Spinlock);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventMultiOs2Wait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventMultiOs2Wait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventMultiOs2Wait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventMultiGetResolution(void)
+{
+ return 32000000; /* 32ms */
+}
+
+
+RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
+{
+ return true;
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/semfastmutex-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/semfastmutex-r0drv-os2.cpp
new file mode 100644
index 00000000..14daa32e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/semfastmutex-r0drv-os2.cpp
@@ -0,0 +1,149 @@
+/* $Id: semfastmutex-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include <iprt/semaphore.h>
+#include <iprt/errcore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the OS/2 KEE mutex semaphore.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The KEE mutex. */
+ MutexLock_t Mtx;
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ AssertCompile(sizeof(RTSEMFASTMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phFastMtx, VERR_INVALID_POINTER);
+
+ PRTSEMFASTMUTEXINTERNAL pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ KernAllocMutexLock(&pThis->Mtx);
+
+ *phFastMtx = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ KernFreeMutexLock(&pThis->Mtx);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ KernRequestExclusiveMutex(&pThis->Mtx);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+
+ KernReleaseExclusiveMutex(&pThis->Mtx);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/spinlock-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/spinlock-r0drv-os2.cpp
new file mode 100644
index 00000000..92d0b144
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/spinlock-r0drv-os2.cpp
@@ -0,0 +1,165 @@
+/* $Id: spinlock-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include <iprt/spinlock.h>
+#include <iprt/errcore.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#ifdef RT_STRICT
+# include <iprt/asm-amd64-x86.h>
+#endif
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the SpinLock_t type.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Spinlock creation flags. */
+ uint32_t fFlags;
+ /** The OS/2 spinlock structure. */
+ SpinLock_t Spinlock;
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+ RT_NOREF(pszName);
+
+ /*
+ * Allocate.
+ */
+ AssertCompile(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize & return.
+ */
+ pThis->u32Magic = RTSPINLOCK_MAGIC;
+ pThis->fFlags = fFlags;
+ KernAllocSpinLock(&pThis->Spinlock);
+ *pSpinlock = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertMsgReturn(pThis->u32Magic == RTSPINLOCK_MAGIC,
+ ("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Make the lock invalid and release the memory.
+ */
+ ASMAtomicIncU32(&pThis->u32Magic);
+ KernFreeSpinLock(&pThis->Spinlock);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ KernAcquireSpinLock(&pThis->Spinlock);
+ Assert(!ASMIntAreEnabled()); /** @todo verify that interrupts are disabled. */
+}
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ KernReleaseSpinLock(&pThis->Spinlock);
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/the-os2-kernel.h b/src/VBox/Runtime/r0drv/os2/the-os2-kernel.h
new file mode 100644
index 00000000..5380aba1
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/the-os2-kernel.h
@@ -0,0 +1,93 @@
+/* $Id: the-os2-kernel.h $ */
+/** @file
+ * IPRT - Ring-0 Driver, The OS/2 Kernel Headers.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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.
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r0drv_os2_the_os2_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_os2_the_os2_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+#define INCL_ERRORS
+#define INCL_DOSSEMAPHORES /* for SEM_INDEFINITE_WAIT */
+#undef RT_MAX
+#include <os2ddk/bsekee.h>
+#include <os2ddk/devhlp.h>
+#undef RT_MAX
+
+RT_C_DECLS_BEGIN
+
+extern PCDOSTABLE g_pDosTable;
+extern PCDOSTABLE2 g_pDosTable2;
+extern PGINFOSEG g_pGIS;
+extern RTFAR16 g_fpLIS;
+
+RTR0DECL(void *) RTR0Os2Virt2Flat(RTFAR16 fp);
+DECLASM(int) RTR0Os2DHQueryDOSVar(uint8_t iVar, uint16_t iSub, PRTFAR16 pfp);
+DECLASM(int) RTR0Os2DHVMGlobalToProcess(ULONG fFlags, PVOID pvR0, ULONG cb, PPVOID ppvR3);
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_os2_the_os2_kernel_h */
diff --git a/src/VBox/Runtime/r0drv/os2/thread-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/thread-r0drv-os2.cpp
new file mode 100644
index 00000000..9b6ff7da
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/thread-r0drv-os2.cpp
@@ -0,0 +1,225 @@
+/* $Id: thread-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Threads (Part 1), Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#include <iprt/asm.h>
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mp.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Per-cpu preemption counters. */
+static int32_t volatile g_acPreemptDisabled[256];
+
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ PLINFOSEG pLIS = (PLINFOSEG)RTR0Os2Virt2Flat(g_fpLIS);
+ AssertMsgReturn(pLIS, ("g_fpLIS=%04x:%04x - logging too early again?\n", g_fpLIS.sel, g_fpLIS.off), NIL_RTNATIVETHREAD);
+ return pLIS->tidCurrent | (pLIS->pidCurrent << 16);
+}
+
+
+static int rtR0ThreadOs2SleepCommon(RTMSINTERVAL cMillies)
+{
+ int rc = KernBlock((ULONG)RTThreadSleep,
+ cMillies == RT_INDEFINITE_WAIT ? SEM_INDEFINITE_WAIT : cMillies,
+ 0, NULL, NULL);
+ switch (rc)
+ {
+ case NO_ERROR:
+ return VINF_SUCCESS;
+ case ERROR_TIMEOUT:
+ return VERR_TIMEOUT;
+ case ERROR_INTERRUPT:
+ return VERR_INTERRUPTED;
+ default:
+ AssertMsgFailed(("%d\n", rc));
+ return VERR_NO_TRANSLATION;
+ }
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadOs2SleepCommon(cMillies);
+}
+
+
+RTDECL(int) RTThreadSleepNoBlock(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadOs2SleepCommon(cMillies);
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ /** @todo implement me (requires a devhelp) */
+ return false;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+ int32_t c = g_acPreemptDisabled[ASMGetApicId()];
+ AssertMsg(c >= 0 && c < 32, ("%d\n", c));
+ return c == 0
+ && ASMIntAreEnabled();
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+
+ union
+ {
+ RTFAR16 fp;
+ uint8_t fResched;
+ } u;
+ int rc = RTR0Os2DHQueryDOSVar(DHGETDOSV_YIELDFLAG, 0, &u.fp);
+ AssertReturn(rc == 0, false);
+ if (u.fResched)
+ return true;
+
+ /** @todo Check if DHGETDOSV_YIELDFLAG includes TCYIELDFLAG. */
+ rc = RTR0Os2DHQueryDOSVar(DHGETDOSV_TCYIELDFLAG, 0, &u.fp);
+ AssertReturn(rc == 0, false);
+ if (u.fResched)
+ return true;
+ return false;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* yes, RTThreadPreemptIsPending is reliable. */
+ return true;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+ /* no kernel preemption on OS/2. */
+ return false;
+}
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ Assert(pState->u32Reserved == 0);
+
+ /* No preemption on OS/2, so do our own accounting. */
+ int32_t c = ASMAtomicIncS32(&g_acPreemptDisabled[ASMGetApicId()]);
+ AssertMsg(c > 0 && c < 32, ("%d\n", c));
+ pState->u32Reserved = c;
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+}
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ AssertMsg(pState->u32Reserved > 0 && pState->u32Reserved < 32, ("%d\n", pState->u32Reserved));
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+
+ /* No preemption on OS/2, so do our own accounting. */
+ int32_t volatile *pc = &g_acPreemptDisabled[ASMGetApicId()];
+ AssertMsg(pState->u32Reserved == (uint32_t)*pc, ("uchDummy=%d *pc=%d \n", pState->u32Reserved, *pc));
+ ASMAtomicUoWriteS32(pc, pState->u32Reserved - 1);
+ pState->u32Reserved = 0;
+}
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD); NOREF(hThread);
+
+ union
+ {
+ RTFAR16 fp;
+ uint8_t cInterruptLevel;
+ } u;
+ /** @todo OS/2: verify the usage of DHGETDOSV_INTERRUPTLEV. */
+ int rc = RTR0Os2DHQueryDOSVar(DHGETDOSV_INTERRUPTLEV, 0, &u.fp);
+ AssertReturn(rc == 0, true);
+
+ return u.cInterruptLevel > 0;
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/thread2-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/thread2-r0drv-os2.cpp
new file mode 100644
index 00000000..1b484343
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/thread2-r0drv-os2.cpp
@@ -0,0 +1,120 @@
+/* $Id: thread2-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, Generic Stubs.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+#include "internal/thread.h"
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative(RTThreadNativeSelf());
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ NOREF(pThread);
+ NOREF(enmType);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+ NOREF(pNativeThread);
+ NOREF(pThreadInt);
+ return VERR_NOT_IMPLEMENTED;
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/time-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/time-r0drv-os2.cpp
new file mode 100644
index 00000000..283a87bd
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/time-r0drv-os2.cpp
@@ -0,0 +1,126 @@
+/* $Id: time-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include <iprt/time.h>
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ /** @remark OS/2 Ring-0: will wrap after 48 days. */
+ return g_pGIS->msecs * UINT64_C(1000000);
+}
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ /** @remark OS/2 Ring-0: will wrap after 48 days. */
+ return g_pGIS->msecs;
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ /** @remark OS/2 Ring-0: will wrap after 48 days. */
+ return g_pGIS->msecs * UINT64_C(1000000);
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ /** @remark OS/2 Ring-0: will wrap after 48 days. */
+ return g_pGIS->msecs;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ /*
+ * Get the seconds since the unix epoch (local time) and current hundredths.
+ */
+ GINFOSEG volatile *pGIS = (GINFOSEG volatile *)g_pGIS;
+ UCHAR uchHundredths;
+ ULONG ulSeconds;
+ do
+ {
+ uchHundredths = pGIS->hundredths;
+ ulSeconds = pGIS->time;
+ } while ( uchHundredths == pGIS->hundredths
+ && ulSeconds == pGIS->time);
+
+ /*
+ * Combine the two and convert to UCT (later).
+ */
+ uint64_t u64 = ulSeconds * UINT64_C(1000000000) + (uint32_t)uchHundredths * 10000000;
+ /** @todo convert from local to UCT. */
+
+ /** @remark OS/2 Ring-0: Currently returns local time instead of UCT. */
+ return RTTimeSpecSetNano(pTime, u64);
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/timer-r0drv-os2.cpp b/src/VBox/Runtime/r0drv/os2/timer-r0drv-os2.cpp
new file mode 100644
index 00000000..7a332cd5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/timer-r0drv-os2.cpp
@@ -0,0 +1,419 @@
+/* $Id: timer-r0drv-os2.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, OS/2.
+ */
+
+/*
+ * Contributed by knut st. osmundsen.
+ *
+ * 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 code is based on:
+ *
+ * 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 *
+*********************************************************************************************************************************/
+#include "the-os2-kernel.h"
+
+#include <iprt/timer.h>
+#include <iprt/time.h>
+#include <iprt/spinlock.h>
+#include <iprt/err.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/alloc.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of an OS/2 timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** The next timer in the timer list. */
+ PRTTIMER pNext;
+ /** Flag indicating the timer is suspended. */
+ uint8_t volatile fSuspended;
+ /** Cleared at the start of timer processing, set when calling pfnTimer.
+ * If any timer changes occurs while doing the callback this will be used to resume the cycle. */
+ bool fDone;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** User argument. */
+ void *pvUser;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t u64NanoInterval;
+ /** The start of the current run.
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64StartTS;
+ /** The start of the current run.
+ * This is used to calculate when the timer ought to fire the next time. */
+ uint64_t volatile u64NextTS;
+ /** The current tick number (since u64StartTS). */
+ uint64_t volatile iTick;
+} RTTIMER;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Spinlock protecting the timers. */
+static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK;
+/** The timer head. */
+static PRTTIMER volatile g_pTimerHead = NULL;
+/** The number of active timers. */
+static uint32_t volatile g_cActiveTimers = 0;
+/** The number of active timers. */
+static uint32_t volatile g_cTimers = 0;
+/** The change number.
+ * This is used to detect list changes during the timer callback loop. */
+static uint32_t volatile g_u32ChangeNo;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+DECLASM(void) rtTimerOs2Tick(void);
+DECLASM(int) rtTimerOs2Arm(void);
+DECLASM(int) rtTimerOs2Dearm(void);
+RT_C_DECLS_END
+
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ *ppTimer = NULL;
+
+ /*
+ * We don't support the fancy MP features.
+ */
+ if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Lazy initialize the spinlock.
+ */
+ if (g_Spinlock == NIL_RTSPINLOCK)
+ {
+ RTSPINLOCK Spinlock;
+ int rc = RTSpinlockCreate(&Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTTimerOS2");
+ AssertRCReturn(rc, rc);
+ //bool fRc;
+ //ASMAtomicCmpXchgSize(&g_Spinlock, Spinlock, NIL_RTSPINLOCK, fRc);
+ //if (!fRc)
+ if (!ASMAtomicCmpXchgPtr((void * volatile *)&g_Spinlock, Spinlock, NIL_RTSPINLOCK))
+ RTSpinlockDestroy(Spinlock);
+ }
+
+ /*
+ * Allocate and initialize the timer handle.
+ */
+ PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
+ if (!pTimer)
+ return VERR_NO_MEMORY;
+
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->pNext = NULL;
+ pTimer->fSuspended = true;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->u64NanoInterval = u64NanoInterval;
+ pTimer->u64StartTS = 0;
+
+ /*
+ * Insert the timer into the list (LIFO atm).
+ */
+ RTSpinlockAcquire(g_Spinlock);
+ g_u32ChangeNo++;
+ pTimer->pNext = g_pTimerHead;
+ g_pTimerHead = pTimer;
+ g_cTimers++;
+ RTSpinlockRelease(g_Spinlock);
+
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Validates the timer handle.
+ *
+ * @returns true if valid, false if invalid.
+ * @param pTimer The handle.
+ */
+DECLINLINE(bool) rtTimerIsValid(PRTTIMER pTimer)
+{
+ AssertPtrReturn(pTimer, false);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, false);
+ return true;
+}
+
+
+RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ /* It's ok to pass NULL pointer. */
+ if (pTimer == /*NIL_RTTIMER*/ NULL)
+ return VINF_SUCCESS;
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Remove it from the list.
+ */
+ RTSpinlockAcquire(g_Spinlock);
+ g_u32ChangeNo++;
+ if (g_pTimerHead == pTimer)
+ g_pTimerHead = pTimer->pNext;
+ else
+ {
+ PRTTIMER pPrev = g_pTimerHead;
+ while (pPrev->pNext != pTimer)
+ {
+ pPrev = pPrev->pNext;
+ if (RT_UNLIKELY(!pPrev))
+ {
+ RTSpinlockRelease(g_Spinlock);
+ return VERR_INVALID_HANDLE;
+ }
+ }
+ pPrev->pNext = pTimer->pNext;
+ }
+ Assert(g_cTimers > 0);
+ g_cTimers--;
+ if (!pTimer->fSuspended)
+ {
+ Assert(g_cActiveTimers > 0);
+ g_cActiveTimers--;
+ if (!g_cActiveTimers)
+ rtTimerOs2Dearm();
+ }
+ RTSpinlockRelease(g_Spinlock);
+
+ /*
+ * Free the associated resources.
+ */
+ pTimer->u32Magic++;
+ RTMemFree(pTimer);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ if (!pTimer->fSuspended)
+ return VERR_TIMER_ACTIVE;
+
+ /*
+ * Calc when it should start firing and give the thread a kick so it get going.
+ */
+ u64First += RTTimeNanoTS();
+
+ RTSpinlockAcquire(g_Spinlock);
+ g_u32ChangeNo++;
+ if (!g_cActiveTimers)
+ {
+ int rc = rtTimerOs2Arm();
+ if (RT_FAILURE(rc))
+ {
+ RTSpinlockRelease(g_Spinlock);
+ return rc;
+ }
+ }
+ g_cActiveTimers++;
+ pTimer->fSuspended = false;
+ pTimer->fDone = true; /* next tick, not current! */
+ pTimer->iTick = 0;
+ pTimer->u64StartTS = u64First;
+ pTimer->u64NextTS = u64First;
+ RTSpinlockRelease(g_Spinlock);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ if (pTimer->fSuspended)
+ return VERR_TIMER_SUSPENDED;
+
+ /*
+ * Suspend the timer.
+ */
+ RTSpinlockAcquire(g_Spinlock);
+ g_u32ChangeNo++;
+ pTimer->fSuspended = true;
+ Assert(g_cActiveTimers > 0);
+ g_cActiveTimers--;
+ if (!g_cActiveTimers)
+ rtTimerOs2Dearm();
+ RTSpinlockRelease(g_Spinlock);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ if (!rtTimerIsValid(pTimer))
+ return VERR_INVALID_HANDLE;
+ RT_NOREF(u64NanoInterval);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLASM(void) rtTimerOs2Tick(void)
+{
+ /*
+ * Query the current time and then take the lock.
+ */
+ const uint64_t u64NanoTS = RTTimeNanoTS();
+
+ RTSpinlockAcquire(g_Spinlock);
+
+ /*
+ * Clear the fDone flag.
+ */
+ PRTTIMER pTimer;
+ for (pTimer = g_pTimerHead; pTimer; pTimer = pTimer->pNext)
+ pTimer->fDone = false;
+
+ /*
+ * Walk the timer list and do the callbacks for any active timer.
+ */
+ uint32_t u32CurChangeNo = g_u32ChangeNo;
+ pTimer = g_pTimerHead;
+ while (pTimer)
+ {
+ PRTTIMER pNext = pTimer->pNext;
+ if ( !pTimer->fSuspended
+ && !pTimer->fDone
+ && pTimer->u64NextTS <= u64NanoTS)
+ {
+ pTimer->fDone = true;
+ pTimer->iTick++;
+
+ /* calculate the next timeout */
+ if (!pTimer->u64NanoInterval)
+ pTimer->fSuspended = true;
+ else
+ {
+ pTimer->u64NextTS = pTimer->u64StartTS + pTimer->iTick * pTimer->u64NanoInterval;
+ if (pTimer->u64NextTS < u64NanoTS)
+ pTimer->u64NextTS = u64NanoTS + RTTimerGetSystemGranularity() / 2;
+ }
+
+ /* do the callout */
+ PFNRTTIMER pfnTimer = pTimer->pfnTimer;
+ void *pvUser = pTimer->pvUser;
+ RTSpinlockRelease(g_Spinlock);
+ pfnTimer(pTimer, pvUser, pTimer->iTick);
+
+ RTSpinlockAcquire(g_Spinlock);
+
+ /* check if anything changed. */
+ if (u32CurChangeNo != g_u32ChangeNo)
+ {
+ u32CurChangeNo = g_u32ChangeNo;
+ pNext = g_pTimerHead;
+ }
+ }
+
+ /* next */
+ pTimer = pNext;
+ }
+
+ RTSpinlockRelease(g_Spinlock);
+}
+
+
+RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
+{
+ return 32000000; /* 32ms */
+}
+
+
+RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
+{
+ RT_NOREF(u32Request, pu32Granted);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
+{
+ RT_NOREF(u32Granted);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(bool) RTTimerCanDoHighResolution(void)
+{
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/os2/timerA-r0drv-os2.asm b/src/VBox/Runtime/r0drv/os2/timerA-r0drv-os2.asm
new file mode 100644
index 00000000..49b2a86a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/os2/timerA-r0drv-os2.asm
@@ -0,0 +1,252 @@
+; $Id: timerA-r0drv-os2.asm $
+;; @file
+; IPRT - DevHelp_VMGlobalToProcess, Ring-0 Driver, OS/2.
+;
+
+;
+; Contributed by knut st. osmundsen.
+;
+; 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 code is based on:
+;
+; 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"
+%include "iprt/err.mac"
+
+
+;*******************************************************************************
+;* External Symbols *
+;*******************************************************************************
+extern KernThunkStackTo32
+extern KernThunkStackTo16
+extern NAME(rtTimerOs2Tick)
+extern NAME(RTErrConvertFromOS2)
+BEGINDATA16
+extern NAME(g_fpfnDevHlp)
+
+
+;*******************************************************************************
+;* Defined Constants And Macros *
+;*******************************************************************************
+%define DevHlp_SetTimer 01dh
+%define DevHlp_ResetTimer 01eh
+
+
+BEGINCODE
+
+;;
+; Arms the our OS/2 timer.
+;
+; @returns IPRT status code.
+;
+BEGINPROC_EXPORTED rtTimerOs2Arm
+ call KernThunkStackTo16
+ push ebp
+ mov ebp, esp
+
+ ; jump to the 16-bit code.
+ ;jmp far dword NAME(rtTimerOs2Arm_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(rtTimerOs2Arm_16) wrt CODE16
+ dw CODE16
+BEGINCODE16
+GLOBALNAME rtTimerOs2Arm_16
+
+ ; setup and do the call
+ push ds
+ push es
+ mov dx, DATA16
+ mov ds, dx
+ mov es, dx
+
+ mov ax, NAME(rtTimerOs2TickAsm) wrt CODE16
+ mov dl, DevHlp_SetTimer
+ call far [NAME(g_fpfnDevHlp)]
+
+ pop es
+ pop ds
+
+ ;jmp far dword NAME(rtTimerOs2Arm_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(rtTimerOs2Arm_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+BEGINCODE
+GLOBALNAME rtTimerOs2Arm_32
+ jc .error
+ xor eax, eax
+.done:
+
+ leave
+ push eax
+ call KernThunkStackTo32
+ pop eax
+ ret
+
+ ; convert the error code.
+.error:
+ and eax, 0ffffh
+ call NAME(RTErrConvertFromOS2)
+ jmp .done
+ENDPROC rtTimerOs2Arm
+
+
+;;
+; Dearms the our OS/2 timer.
+;
+; @returns IPRT status code.
+;
+BEGINPROC_EXPORTED rtTimerOs2Dearm
+ call KernThunkStackTo16
+ push ebp
+ mov ebp, esp
+
+ ; jump to the 16-bit code.
+ ;jmp far dword NAME(rtTimerOs2Dearm_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(rtTimerOs2Dearm_16) wrt CODE16
+ dw CODE16
+BEGINCODE16
+GLOBALNAME rtTimerOs2Dearm_16
+
+ ; setup and do the call
+ push ds
+ push es
+ mov dx, DATA16
+ mov ds, dx
+ mov es, dx
+
+ mov ax, NAME(rtTimerOs2TickAsm) wrt CODE16
+ mov dl, DevHlp_ResetTimer
+ call far [NAME(g_fpfnDevHlp)]
+
+ pop es
+ pop ds
+
+ ;jmp far dword NAME(rtTimerOs2Dearm_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(rtTimerOs2Dearm_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+BEGINCODE
+GLOBALNAME rtTimerOs2Dearm_32
+ jc .error
+ xor eax, eax
+.done:
+
+ ; epilogue
+ leave
+ push eax
+ call KernThunkStackTo32
+ pop eax
+ ret
+
+ ; convert the error code.
+.error:
+ and eax, 0ffffh
+ call NAME(RTErrConvertFromOS2)
+ jmp .done
+ENDPROC rtTimerOs2Dearm
+
+
+BEGINCODE16
+
+;;
+; OS/2 timer tick callback.
+;
+BEGINPROC rtTimerOs2TickAsm
+ push ds
+ push es
+ push ecx
+ push edx
+ push eax
+
+ mov ax, DATA32 wrt FLAT
+ mov ds, ax
+ mov es, ax
+
+ ;jmp far dword NAME(rtTimerOs2TickAsm_32) wrt FLAT
+ db 066h
+ db 0eah
+ dd NAME(rtTimerOs2TickAsm_32) ;wrt FLAT
+ dw TEXT32 wrt FLAT
+BEGINCODE
+GLOBALNAME rtTimerOs2TickAsm_32
+ call KernThunkStackTo32
+ call NAME(rtTimerOs2Tick)
+ call KernThunkStackTo16
+
+ ;jmp far dword NAME(rtTimerOs2TickAsm_16) wrt CODE16
+ db 066h
+ db 0eah
+ dw NAME(rtTimerOs2TickAsm_16) wrt CODE16
+ dw CODE16
+BEGINCODE16
+GLOBALNAME rtTimerOs2TickAsm_16
+
+ pop eax
+ pop edx
+ pop ecx
+ pop es
+ pop ds
+ retf
+ENDPROC rtTimerOs2TickAsm
diff --git a/src/VBox/Runtime/r0drv/power-r0drv.h b/src/VBox/Runtime/r0drv/power-r0drv.h
new file mode 100644
index 00000000..115eb548
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/power-r0drv.h
@@ -0,0 +1,54 @@
+/* $Id: power-r0drv.h $ */
+/** @file
+ * IPRT - Power Management, Ring-0 Driver, 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 IPRT_INCLUDED_SRC_r0drv_power_r0drv_h
+#define IPRT_INCLUDED_SRC_r0drv_power_r0drv_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/power.h>
+
+RT_C_DECLS_BEGIN
+
+/* Called from initterm-r0drv.cpp: */
+DECLHIDDEN(int) rtR0PowerNotificationInit(void);
+DECLHIDDEN(void) rtR0PowerNotificationTerm(void);
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_power_r0drv_h */
+
diff --git a/src/VBox/Runtime/r0drv/powernotification-r0drv.c b/src/VBox/Runtime/r0drv/powernotification-r0drv.c
new file mode 100644
index 00000000..19912184
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/powernotification-r0drv.c
@@ -0,0 +1,328 @@
+/* $Id: powernotification-r0drv.c $ */
+/** @file
+ * IPRT - Power Management, Ring-0 Driver, Event Notifications.
+ */
+
+/*
+ * 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 <iprt/power.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "r0drv/mp-r0drv.h"
+#include "r0drv/power-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Notification registration record tracking
+ * RTPowerRegisterNotification() calls.
+ */
+typedef struct RTPOWERNOTIFYREG
+{
+ /** Pointer to the next record. */
+ struct RTPOWERNOTIFYREG * volatile pNext;
+ /** The callback. */
+ PFNRTPOWERNOTIFICATION pfnCallback;
+ /** The user argument. */
+ void *pvUser;
+ /** Bit mask indicating whether we've done this callback or not. */
+ uint8_t bmDone[sizeof(void *)];
+} RTPOWERNOTIFYREG;
+/** Pointer to a registration record. */
+typedef RTPOWERNOTIFYREG *PRTPOWERNOTIFYREG;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The spinlock protecting the list. */
+static RTSPINLOCK volatile g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
+/** List of callbacks, in registration order. */
+static PRTPOWERNOTIFYREG volatile g_pRTPowerCallbackHead = NULL;
+/** The current done bit. */
+static uint32_t volatile g_iRTPowerDoneBit;
+/** The list generation.
+ * This is increased whenever the list has been modified. The callback routine
+ * make use of this to avoid having restart at the list head after each callback. */
+static uint32_t volatile g_iRTPowerGeneration;
+
+
+
+
+RTDECL(int) RTPowerSignalEvent(RTPOWEREVENT enmEvent)
+{
+ PRTPOWERNOTIFYREG pCur;
+ RTSPINLOCK hSpinlock;
+
+ /*
+ * This is a little bit tricky as we cannot be holding the spinlock
+ * while calling the callback. This means that the list might change
+ * while we're walking it, and that multiple events might be running
+ * concurrently (depending on the OS).
+ *
+ * So, the first measure is to employ a 32-bitmask for each
+ * record where we'll use a bit that rotates for each call to
+ * this function to indicate which records that has been
+ * processed. This will take care of both changes to the list
+ * and a reasonable amount of concurrent events.
+ *
+ * In order to avoid having to restart the list walks for every
+ * callback we make, we'll make use a list generation number that is
+ * incremented everytime the list is changed. So, if it remains
+ * unchanged over a callback we can safely continue the iteration.
+ */
+ uint32_t iDone = ASMAtomicIncU32(&g_iRTPowerDoneBit);
+ iDone %= RT_SIZEOFMEMB(RTPOWERNOTIFYREG, bmDone) * 8;
+
+ hSpinlock = g_hRTPowerNotifySpinLock;
+ if (hSpinlock == NIL_RTSPINLOCK)
+ return VERR_ACCESS_DENIED;
+ RTSpinlockAcquire(hSpinlock);
+
+ /* Clear the bit. */
+ for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
+ ASMAtomicBitClear(&pCur->bmDone[0], iDone);
+
+ /* Iterate the records and perform the callbacks. */
+ do
+ {
+ uint32_t const iGeneration = ASMAtomicUoReadU32(&g_iRTPowerGeneration);
+
+ pCur = g_pRTPowerCallbackHead;
+ while (pCur)
+ {
+ if (!ASMAtomicBitTestAndSet(&pCur->bmDone[0], iDone))
+ {
+ PFNRTPOWERNOTIFICATION pfnCallback = pCur->pfnCallback;
+ void *pvUser = pCur->pvUser;
+ pCur = pCur->pNext;
+ RTSpinlockRelease(g_hRTPowerNotifySpinLock);
+
+ pfnCallback(enmEvent, pvUser);
+
+ /* carefully require the lock here, see RTR0MpNotificationTerm(). */
+ hSpinlock = g_hRTPowerNotifySpinLock;
+ if (hSpinlock == NIL_RTSPINLOCK)
+ return VERR_ACCESS_DENIED;
+ RTSpinlockAcquire(hSpinlock);
+ if (ASMAtomicUoReadU32(&g_iRTPowerGeneration) != iGeneration)
+ break;
+ }
+ else
+ pCur = pCur->pNext;
+ }
+ } while (pCur);
+
+ RTSpinlockRelease(hSpinlock);
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTPowerSignalEvent);
+
+
+RTDECL(int) RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
+{
+ PRTPOWERNOTIFYREG pCur;
+ PRTPOWERNOTIFYREG pNew;
+
+ /*
+ * Validation.
+ */
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ RTSpinlockAcquire(g_hRTPowerNotifySpinLock);
+ for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
+ if ( pCur->pvUser == pvUser
+ && pCur->pfnCallback == pfnCallback)
+ break;
+ RTSpinlockRelease(g_hRTPowerNotifySpinLock);
+ AssertMsgReturn(!pCur, ("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
+
+ /*
+ * Allocate a new record and attempt to insert it.
+ */
+ pNew = (PRTPOWERNOTIFYREG)RTMemAlloc(sizeof(*pNew));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+
+ pNew->pNext = NULL;
+ pNew->pfnCallback = pfnCallback;
+ pNew->pvUser = pvUser;
+ memset(&pNew->bmDone[0], 0xff, sizeof(pNew->bmDone));
+
+ RTSpinlockAcquire(g_hRTPowerNotifySpinLock);
+
+ pCur = g_pRTPowerCallbackHead;
+ if (!pCur)
+ g_pRTPowerCallbackHead = pNew;
+ else
+ {
+ for (pCur = g_pRTPowerCallbackHead; ; pCur = pCur->pNext)
+ if ( pCur->pvUser == pvUser
+ && pCur->pfnCallback == pfnCallback)
+ break;
+ else if (!pCur->pNext)
+ {
+ pCur->pNext = pNew;
+ pCur = NULL;
+ break;
+ }
+ }
+
+ ASMAtomicIncU32(&g_iRTPowerGeneration);
+
+ RTSpinlockRelease(g_hRTPowerNotifySpinLock);
+
+ /* duplicate? */
+ if (pCur)
+ {
+ RTMemFree(pCur);
+ AssertMsgFailedReturn(("pCur=%p pfnCallback=%p pvUser=%p\n", pCur, pfnCallback, pvUser), VERR_ALREADY_EXISTS);
+ }
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTPowerNotificationRegister);
+
+
+RTDECL(int) RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser)
+{
+ PRTPOWERNOTIFYREG pPrev;
+ PRTPOWERNOTIFYREG pCur;
+
+ /*
+ * Validation.
+ */
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertReturn(g_hRTPowerNotifySpinLock != NIL_RTSPINLOCK, VERR_WRONG_ORDER);
+ RT_ASSERT_INTS_ON();
+
+ /*
+ * Find and unlink the record from the list.
+ */
+ RTSpinlockAcquire(g_hRTPowerNotifySpinLock);
+ pPrev = NULL;
+ for (pCur = g_pRTPowerCallbackHead; pCur; pCur = pCur->pNext)
+ {
+ if ( pCur->pvUser == pvUser
+ && pCur->pfnCallback == pfnCallback)
+ break;
+ pPrev = pCur;
+ }
+ if (pCur)
+ {
+ if (pPrev)
+ pPrev->pNext = pCur->pNext;
+ else
+ g_pRTPowerCallbackHead = pCur->pNext;
+ ASMAtomicIncU32(&g_iRTPowerGeneration);
+ }
+ RTSpinlockRelease(g_hRTPowerNotifySpinLock);
+
+ if (!pCur)
+ return VERR_NOT_FOUND;
+
+ /*
+ * Invalidate and free the record.
+ */
+ pCur->pNext = NULL;
+ pCur->pfnCallback = NULL;
+ RTMemFree(pCur);
+
+ return VINF_SUCCESS;
+}
+RT_EXPORT_SYMBOL(RTPowerNotificationDeregister);
+
+
+DECLHIDDEN(int) rtR0PowerNotificationInit(void)
+{
+ int rc = RTSpinlockCreate((PRTSPINLOCK)&g_hRTPowerNotifySpinLock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "RTR0Power");
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo OS specific init here */
+ return rc;
+#if 0
+ RTSpinlockDestroy(g_hRTPowerNotifySpinLock);
+ g_hRTPowerNotifySpinLock = NIL_RTSPINLOCK;
+#endif
+ }
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtR0PowerNotificationTerm(void)
+{
+ PRTPOWERNOTIFYREG pHead;
+ RTSPINLOCK hSpinlock = g_hRTPowerNotifySpinLock;
+ AssertReturnVoid(hSpinlock != NIL_RTSPINLOCK);
+
+ /** @todo OS specific term here */
+
+ /* pick up the list and the spinlock. */
+ RTSpinlockAcquire(hSpinlock);
+ ASMAtomicWriteHandle(&g_hRTPowerNotifySpinLock, NIL_RTSPINLOCK);
+ pHead = g_pRTPowerCallbackHead;
+ g_pRTPowerCallbackHead = NULL;
+ ASMAtomicIncU32(&g_iRTPowerGeneration);
+ RTSpinlockRelease(hSpinlock);
+
+ /* free the list. */
+ while (pHead)
+ {
+ PRTPOWERNOTIFYREG pFree = pHead;
+ pHead = pHead->pNext;
+
+ pFree->pNext = NULL;
+ pFree->pfnCallback = NULL;
+ RTMemFree(pFree);
+ }
+
+ RTSpinlockDestroy(hSpinlock);
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/Makefile.kup b/src/VBox/Runtime/r0drv/solaris/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/Makefile.kup
diff --git a/src/VBox/Runtime/r0drv/solaris/RTLogWriteDebugger-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/RTLogWriteDebugger-r0drv-solaris.c
new file mode 100644
index 00000000..1ac8ed3d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/RTLogWriteDebugger-r0drv-solaris.c
@@ -0,0 +1,76 @@
+/* $Id: RTLogWriteDebugger-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Log To Debugger, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/log.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ if (pch[cb] != '\0')
+ AssertBreakpoint();
+
+ /* cmn_err() acquires adaptive mutexes. Not preemption safe, see @bugref{6657}. */
+ if (!RTThreadPreemptIsEnabled(NIL_RTTHREAD))
+ return;
+
+ if ( !g_frtSolSplSetsEIF
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ || ASMIntAreEnabled()
+#else
+/* PORTME: Check if interrupts are enabled, if applicable. */
+#endif
+ )
+ {
+ cmn_err(CE_CONT, pch);
+ }
+
+ return;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/RTMpPokeCpu-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/RTMpPokeCpu-r0drv-solaris.c
new file mode 100644
index 00000000..713c9c63
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/RTMpPokeCpu-r0drv-solaris.c
@@ -0,0 +1,60 @@
+/* $Id: RTMpPokeCpu-r0drv-solaris.c $ */
+/** @file
+ * IPRT - RTMpPokeCpu, Solaris 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 *
+*********************************************************************************************************************************/
+#include "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mp.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+
+RTDECL(int) RTMpPokeCpu(RTCPUID idCpu)
+{
+ RT_ASSERT_INTS_ON();
+ if (idCpu < ncpus)
+ poke_cpu(idCpu);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/alloc-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/alloc-r0drv-solaris.c
new file mode 100644
index 00000000..9c0e9765
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/alloc-r0drv-solaris.c
@@ -0,0 +1,198 @@
+/* $Id: alloc-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Memory Allocation, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/thread.h>
+#include "r0drv/alloc-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+static ddi_dma_attr_t s_rtR0SolDmaAttr =
+{
+ DMA_ATTR_V0, /* Version Number */
+ (uint64_t)0, /* Lower limit */
+ (uint64_t)0, /* High limit */
+ (uint64_t)0xffffffff, /* Counter limit */
+ (uint64_t)PAGESIZE, /* Alignment */
+ (uint64_t)PAGESIZE, /* Burst size */
+ (uint64_t)PAGESIZE, /* Effective DMA size */
+ (uint64_t)0xffffffff, /* Max DMA xfer size */
+ (uint64_t)0xffffffff, /* Segment boundary */
+ 1, /* Scatter-gather list length (1 for contiguous) */
+ 1, /* Device granularity */
+ 0 /* Bus-specific flags */
+};
+
+extern void *contig_alloc(size_t cb, ddi_dma_attr_t *pDmaAttr, size_t uAlign, int fCanSleep);
+
+
+/**
+ * OS specific allocation function.
+ */
+DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr)
+{
+ size_t cbAllocated = cb;
+ PRTMEMHDR pHdr;
+ unsigned fKmFlags = fFlags & RTMEMHDR_FLAG_ANY_CTX_ALLOC ? KM_NOSLEEP : KM_SLEEP;
+ if (fFlags & RTMEMHDR_FLAG_ZEROED)
+ pHdr = (PRTMEMHDR)kmem_zalloc(cb + sizeof(*pHdr), fKmFlags);
+ else
+ pHdr = (PRTMEMHDR)kmem_alloc(cb + sizeof(*pHdr), fKmFlags);
+ if (RT_UNLIKELY(!pHdr))
+ {
+ LogRel(("rtMemAllocEx(%u, %#x) failed\n", (unsigned)cb + sizeof(*pHdr), fFlags));
+ return VERR_NO_MEMORY;
+ }
+
+ pHdr->u32Magic = RTMEMHDR_MAGIC;
+ pHdr->fFlags = fFlags;
+ pHdr->cb = cbAllocated;
+ pHdr->cbReq = cb;
+
+ *ppHdr = pHdr;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * OS specific free function.
+ */
+DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr)
+{
+ pHdr->u32Magic += 1;
+ kmem_free(pHdr, pHdr->cb + sizeof(*pHdr));
+}
+
+
+/**
+ * Allocates physical memory which satisfy the given constraints.
+ *
+ * @param uPhysHi The upper physical address limit (inclusive).
+ * @param puPhys Where to store the physical address of the allocated
+ * memory. Optional, can be NULL.
+ * @param cb Size of allocation.
+ * @param uAlignment Alignment.
+ * @param fContig Whether the memory must be physically contiguous or
+ * not.
+ * @returns Virtual address of allocated memory block or NULL if allocation
+ * failed.
+ */
+DECLHIDDEN(void *) rtR0SolMemAlloc(uint64_t uPhysHi, uint64_t *puPhys, size_t cb, uint64_t uAlignment, bool fContig)
+{
+ if ((cb & PAGEOFFSET) != 0)
+ return NULL;
+
+ size_t cPages = (cb + PAGESIZE - 1) >> PAGESHIFT;
+ if (!cPages)
+ return NULL;
+
+ ddi_dma_attr_t DmaAttr = s_rtR0SolDmaAttr;
+ DmaAttr.dma_attr_addr_hi = uPhysHi;
+ DmaAttr.dma_attr_align = uAlignment;
+ if (!fContig)
+ DmaAttr.dma_attr_sgllen = cPages > INT_MAX ? INT_MAX - 1 : cPages;
+ else
+ AssertRelease(DmaAttr.dma_attr_sgllen == 1);
+
+ void *pvMem = contig_alloc(cb, &DmaAttr, PAGESIZE, 1 /* can sleep */);
+ if (!pvMem)
+ {
+ LogRel(("rtR0SolMemAlloc failed. cb=%u Align=%u fContig=%d\n", (unsigned)cb, (unsigned)uAlignment, fContig));
+ return NULL;
+ }
+
+ pfn_t PageFrameNum = hat_getpfnum(kas.a_hat, (caddr_t)pvMem);
+ AssertRelease(PageFrameNum != PFN_INVALID);
+ if (puPhys)
+ *puPhys = (uint64_t)PageFrameNum << PAGESHIFT;
+
+ return pvMem;
+}
+
+
+/**
+ * Frees memory allocated using rtR0SolMemAlloc().
+ *
+ * @param pv The memory to free.
+ * @param cb Size of the memory block
+ */
+DECLHIDDEN(void) rtR0SolMemFree(void *pv, size_t cb)
+{
+ if (RT_LIKELY(pv))
+ g_pfnrtR0Sol_contig_free(pv, cb);
+}
+
+
+RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb)
+{
+ AssertPtrReturn(pPhys, NULL);
+ AssertReturn(cb > 0, NULL);
+ RT_ASSERT_PREEMPTIBLE();
+
+ /* Allocate physically contiguous (< 4GB) page-aligned memory. */
+ uint64_t uPhys;
+ void *pvMem = rtR0SolMemAlloc((uint64_t)_4G - 1, &uPhys, cb, PAGESIZE, true /* fContig */);
+ if (RT_UNLIKELY(!pvMem))
+ {
+ LogRel(("RTMemContAlloc failed to allocate %u bytes\n", cb));
+ return NULL;
+ }
+
+ Assert(uPhys < _4G);
+ *pPhys = uPhys;
+ return pvMem;
+}
+
+
+RTR0DECL(void) RTMemContFree(void *pv, size_t cb)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ rtR0SolMemFree(pv, cb);
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/assert-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/assert-r0drv-solaris.c
new file mode 100644
index 00000000..a1b9dc56
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/assert-r0drv-solaris.c
@@ -0,0 +1,87 @@
+/* $Id: assert-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Assertion Workers, Ring-0 Drivers, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/assert.h>
+
+#include <iprt/asm.h>
+#include <iprt/log.h>
+#include <iprt/stdarg.h>
+#include <iprt/string.h>
+
+#include "internal/assert.h"
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+ uprintf("\r\n!!Assertion Failed!!\r\n"
+ "Expression: %s\r\n"
+ "Location : %s(%d) %s\r\n",
+ pszExpr, pszFile, uLine, pszFunction);
+}
+
+
+DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va)
+{
+ char szMsg[256];
+
+ RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va);
+ szMsg[sizeof(szMsg) - 1] = '\0';
+ uprintf("%s", szMsg);
+
+ NOREF(fInitial);
+}
+
+
+RTR0DECL(void) RTR0AssertPanicSystem(void)
+{
+ const char *psz = &g_szRTAssertMsg2[0];
+ const char *pszEnd = &g_szRTAssertMsg2[sizeof(g_szRTAssertMsg2)];
+ while (psz < pszEnd && (*psz == ' ' || *psz == '\t' || *psz == '\n' || *psz == '\r'))
+ psz++;
+
+ if (psz < pszEnd && *psz)
+ assfail(psz, g_pszRTAssertFile, g_u32RTAssertLine);
+ else
+ assfail(g_szRTAssertMsg1, g_pszRTAssertFile, g_u32RTAssertLine);
+ g_szRTAssertMsg2[0] = '\0';
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/dbgkrnlinfo-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/dbgkrnlinfo-r0drv-solaris.c
new file mode 100644
index 00000000..bc617428
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/dbgkrnlinfo-r0drv-solaris.c
@@ -0,0 +1,349 @@
+/* $Id: dbgkrnlinfo-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Kernel debug information, Ring-0 Driver, Solaris Code.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/dbg.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Solaris kernel debug info instance data.
+ */
+typedef struct RTDBGKRNLINFOINT
+{
+ /** Magic value (RTDBGKRNLINFO_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The number of threads referencing this object. */
+ uint32_t volatile cRefs;
+ /** Pointer to the genunix CTF handle. */
+ ctf_file_t *pGenUnixCTF;
+ /** Pointer to the genunix module handle. */
+ modctl_t *pGenUnixMod;
+} RTDBGKRNLINFOINT;
+/** Pointer to the solaris kernel debug info instance data. */
+typedef struct RTDBGKRNLINFOINT *PRTDBGKRNLINFOINT;
+
+
+/**
+ * Retains a kernel module and opens the CTF data associated with it.
+ *
+ * @param pszModule The name of the module to open.
+ * @param ppMod Where to store the module handle.
+ * @param ppCTF Where to store the module's CTF handle.
+ *
+ * @return IPRT status code.
+ */
+static int rtR0DbgKrnlInfoModRetain(char *pszModule, modctl_t **ppMod, ctf_file_t **ppCTF)
+{
+ AssertPtrReturn(pszModule, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppMod, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppCTF, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ modid_t ModId = mod_name_to_modid(pszModule);
+ if (ModId != -1)
+ {
+ *ppMod = mod_hold_by_id(ModId);
+ if (*ppMod)
+ {
+ /*
+ * Hold mod_lock as ctf_modopen may update the module with uncompressed CTF data.
+ */
+ int err;
+ mutex_enter(&mod_lock);
+ *ppCTF = ctf_modopen(((modctl_t *)*ppMod)->mod_mp, &err);
+ mutex_exit(&mod_lock);
+ mod_release_mod(*ppMod);
+
+ if (*ppCTF)
+ return VINF_SUCCESS;
+ else
+ {
+ LogRel(("rtR0DbgKrnlInfoModRetain: ctf_modopen failed for '%s' err=%d\n", pszModule, err));
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+ }
+ else
+ {
+ LogRel(("rtR0DbgKrnlInfoModRetain: mod_hold_by_id failed for '%s'\n", pszModule));
+ rc = VERR_INTERNAL_ERROR_2;
+ }
+ }
+ else
+ {
+ LogRel(("rtR0DbgKrnlInfoModRetain: mod_name_to_modid failed for '%s'\n", pszModule));
+ rc = VERR_INTERNAL_ERROR;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Releases the kernel module and closes its CTF data.
+ *
+ * @param pMod Pointer to the module handle.
+ * @param pCTF Pointer to the module's CTF handle.
+ */
+static void rtR0DbgKrnlInfoModRelease(modctl_t *pMod, ctf_file_t *pCTF)
+{
+ AssertPtrReturnVoid(pMod);
+ AssertPtrReturnVoid(pCTF);
+
+ ctf_close(pCTF);
+}
+
+
+/**
+ * Helper for opening the specified kernel module.
+ *
+ * @param pszModule The name of the module.
+ * @param ppMod Where to store the module handle.
+ * @param ppCtf Where to store the module's CTF handle.
+ *
+ * @returns Pointer to the CTF structure for the module.
+ */
+static int rtR0DbgKrnlInfoModRetainEx(const char *pszModule, modctl_t **ppMod, ctf_file_t **ppCtf)
+{
+ char *pszMod = RTStrDup(pszModule);
+ if (RT_LIKELY(pszMod))
+ {
+ int rc = rtR0DbgKrnlInfoModRetain(pszMod, ppMod, ppCtf);
+ RTStrFree(pszMod);
+ if (RT_SUCCESS(rc))
+ {
+ AssertPtrReturn(*ppMod, VERR_INTERNAL_ERROR_2);
+ AssertPtrReturn(*ppCtf, VERR_INTERNAL_ERROR_3);
+ }
+ return rc;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoOpen(PRTDBGKRNLINFO phKrnlInfo, uint32_t fFlags)
+{
+ AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phKrnlInfo, VERR_INVALID_POINTER);
+ /* This can be called as part of IPRT init, in which case we have no thread preempt information yet. */
+ if (g_frtSolInitDone)
+ RT_ASSERT_PREEMPTIBLE();
+
+ *phKrnlInfo = NIL_RTDBGKRNLINFO;
+ PRTDBGKRNLINFOINT pThis = (PRTDBGKRNLINFOINT)RTMemAllocZ(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ char szGenUnixModName[] = "genunix";
+ int rc = rtR0DbgKrnlInfoModRetain(szGenUnixModName, &pThis->pGenUnixMod, &pThis->pGenUnixCTF);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->u32Magic = RTDBGKRNLINFO_MAGIC;
+ pThis->cRefs = 1;
+
+ *phKrnlInfo = pThis;
+ return VINF_SUCCESS;
+ }
+
+ LogRel(("RTR0DbgKrnlInfoOpen: rtR0DbgKrnlInfoModRetain failed rc=%d.\n", rc));
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+RTR0DECL(uint32_t) RTR0DbgKrnlInfoRetain(RTDBGKRNLINFO hKrnlInfo)
+{
+ PRTDBGKRNLINFOINT pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+ return cRefs;
+}
+
+
+RTR0DECL(uint32_t) RTR0DbgKrnlInfoRelease(RTDBGKRNLINFO hKrnlInfo)
+{
+ PRTDBGKRNLINFOINT pThis = hKrnlInfo;
+ if (pThis == NIL_RTDBGKRNLINFO)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), UINT32_MAX);
+ if (g_frtSolInitDone)
+ RT_ASSERT_PREEMPTIBLE();
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (cRefs == 0)
+ {
+ pThis->u32Magic = ~RTDBGKRNLINFO_MAGIC;
+ rtR0DbgKrnlInfoModRelease(pThis->pGenUnixMod, pThis->pGenUnixCTF);
+ RTMemFree(pThis);
+ }
+ return cRefs;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQueryMember(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszStructure,
+ const char *pszMember, size_t *poffMember)
+{
+ PRTDBGKRNLINFOINT pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszMember, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszStructure, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(poffMember, VERR_INVALID_PARAMETER);
+ if (g_frtSolInitDone)
+ RT_ASSERT_PREEMPTIBLE();
+
+ ctf_file_t *pCtf = NULL;
+ modctl_t *pMod = NULL;
+ if (!pszModule)
+ {
+ pCtf = pThis->pGenUnixCTF;
+ pMod = pThis->pGenUnixMod;
+ }
+ else
+ {
+ int rc2 = rtR0DbgKrnlInfoModRetainEx(pszModule, &pMod, &pCtf);
+ if (RT_FAILURE(rc2))
+ return rc2;
+ Assert(pMod);
+ Assert(pCtf);
+ }
+
+ int rc = VERR_NOT_FOUND;
+ ctf_id_t TypeIdent = ctf_lookup_by_name(pCtf, pszStructure);
+ if (TypeIdent != CTF_ERR)
+ {
+ ctf_membinfo_t MemberInfo;
+ RT_ZERO(MemberInfo);
+ if (ctf_member_info(pCtf, TypeIdent, pszMember, &MemberInfo) != CTF_ERR)
+ {
+ *poffMember = (MemberInfo.ctm_offset >> 3);
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ if (pszModule)
+ rtR0DbgKrnlInfoModRelease(pMod, pCtf);
+ return rc;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQuerySymbol(RTDBGKRNLINFO hKrnlInfo, const char *pszModule,
+ const char *pszSymbol, void **ppvSymbol)
+{
+ PRTDBGKRNLINFOINT pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszSymbol, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(ppvSymbol, VERR_INVALID_PARAMETER);
+ AssertReturn(!pszModule, VERR_MODULE_NOT_FOUND);
+ if (g_frtSolInitDone)
+ RT_ASSERT_PREEMPTIBLE();
+
+ uintptr_t uValue = kobj_getsymvalue((char *)pszSymbol, 1 /* only kernel */);
+ if (ppvSymbol)
+ *ppvSymbol = (void *)uValue;
+ if (uValue)
+ return VINF_SUCCESS;
+ return VERR_SYMBOL_NOT_FOUND;
+}
+
+
+RTR0DECL(int) RTR0DbgKrnlInfoQuerySize(RTDBGKRNLINFO hKrnlInfo, const char *pszModule, const char *pszType, size_t *pcbType)
+{
+ PRTDBGKRNLINFOINT pThis = hKrnlInfo;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTDBGKRNLINFO_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertPtrReturn(pszType, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbType, VERR_INVALID_PARAMETER);
+ if (g_frtSolInitDone)
+ RT_ASSERT_PREEMPTIBLE();
+
+ modctl_t *pMod = NULL;
+ ctf_file_t *pCtf = NULL;
+ if (!pszModule)
+ {
+ pCtf = pThis->pGenUnixCTF;
+ pMod = pThis->pGenUnixMod;
+ }
+ else
+ {
+ int rc2 = rtR0DbgKrnlInfoModRetainEx(pszModule, &pMod, &pCtf);
+ if (RT_FAILURE(rc2))
+ return rc2;
+ Assert(pMod);
+ Assert(pCtf);
+ }
+
+ int rc = VERR_NOT_FOUND;
+ ctf_id_t TypeIdent = ctf_lookup_by_name(pCtf, pszType);
+ if (TypeIdent != CTF_ERR)
+ {
+ ssize_t cbType = ctf_type_size(pCtf, TypeIdent);
+ if (cbType > 0)
+ {
+ *pcbType = cbType;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_WRONG_TYPE;
+ }
+
+ if (pszModule)
+ rtR0DbgKrnlInfoModRelease(pMod, pCtf);
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/initterm-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/initterm-r0drv-solaris.c
new file mode 100644
index 00000000..4320a775
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/initterm-r0drv-solaris.c
@@ -0,0 +1,310 @@
+/* $Id: initterm-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Initialization & Termination, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include "internal/initterm.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Kernel debug info handle. */
+RTDBGKRNLINFO g_hKrnlDbgInfo;
+/** Indicates that the spl routines (and therefore a bunch of other ones too)
+ * will set EFLAGS::IF and break code that disables interrupts. */
+bool g_frtSolSplSetsEIF = false;
+/** timeout_generic address. */
+PFNSOL_timeout_generic g_pfnrtR0Sol_timeout_generic = NULL;
+/** untimeout_generic address. */
+PFNSOL_untimeout_generic g_pfnrtR0Sol_untimeout_generic = NULL;
+/** cyclic_reprogram address. */
+PFNSOL_cyclic_reprogram g_pfnrtR0Sol_cyclic_reprogram = NULL;
+/** page_noreloc_supported address. */
+PFNSOL_page_noreloc_supported g_pfnrtR0Sol_page_noreloc_supported = NULL;
+/** Whether to use the kernel page freelist. */
+bool g_frtSolUseKflt = false;
+/** Whether we've completed R0 initialization. */
+bool g_frtSolInitDone = false;
+/** Whether to use old-style xc_call interface. */
+bool g_frtSolOldIPI = false;
+/** Whether to use old-style xc_call interface using one ulong_t as the CPU set
+ * representation. */
+bool g_frtSolOldIPIUlong = false;
+/** The xc_call callout table structure. */
+RTR0FNSOLXCCALL g_rtSolXcCall;
+/** Whether to use the old-style installctx()/removectx() routines. */
+bool g_frtSolOldThreadCtx = false;
+/** The thread-context hooks callout table structure. */
+RTR0FNSOLTHREADCTX g_rtSolThreadCtx;
+/** Thread preemption offset in the thread structure. */
+size_t g_offrtSolThreadPreempt;
+/** Thread ID offset in the thread structure. */
+size_t g_offrtSolThreadId;
+/** The interrupt (pinned) thread pointer offset in the thread structure. */
+size_t g_offrtSolThreadIntrThread;
+/** The dispatcher lock pointer offset in the thread structure. */
+size_t g_offrtSolThreadLock;
+/** The process pointer offset in the thread structure. */
+size_t g_offrtSolThreadProc;
+/** Host scheduler preemption offset. */
+size_t g_offrtSolCpuPreempt;
+/** Host scheduler force preemption offset. */
+size_t g_offrtSolCpuForceKernelPreempt;
+/** Whether to use the old-style map_addr() routine. */
+bool g_frtSolOldMapAddr = false;
+/** The map_addr() hooks callout table structure. */
+RTR0FNSOLMAPADDR g_rtSolMapAddr;
+/* Resolve using dl_lookup (remove if no longer relevant for supported S10 versions) */
+extern void contig_free(void *addr, size_t size);
+#pragma weak contig_free
+/** contig_free address. */
+PFNSOL_contig_free g_pfnrtR0Sol_contig_free = contig_free;
+
+DECLHIDDEN(int) rtR0InitNative(void)
+{
+ /*
+ * IPRT has not yet been initialized at this point, so use Solaris' native cmn_err() for logging.
+ */
+ int rc = RTR0DbgKrnlInfoOpen(&g_hKrnlDbgInfo, 0 /* fFlags */);
+ if (RT_SUCCESS(rc))
+ {
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ /*
+ * Detect whether spl*() is preserving the interrupt flag or not.
+ * This is a problem on S10.
+ */
+ RTCCUINTREG uOldFlags = ASMIntDisableFlags();
+ int iOld = splr(DISP_LEVEL);
+ if (ASMIntAreEnabled())
+ g_frtSolSplSetsEIF = true;
+ splx(iOld);
+ if (ASMIntAreEnabled())
+ g_frtSolSplSetsEIF = true;
+ ASMSetFlags(uOldFlags);
+#else
+ /* PORTME: See if the amd64/x86 problem applies to this architecture. */
+#endif
+ /*
+ * Mandatory: Preemption offsets.
+ */
+ rc = RTR0DbgKrnlInfoQueryMember(g_hKrnlDbgInfo, NULL, "cpu_t", "cpu_runrun", &g_offrtSolCpuPreempt);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "Failed to find cpu_t::cpu_runrun!\n");
+ goto errorbail;
+ }
+
+ rc = RTR0DbgKrnlInfoQueryMember(g_hKrnlDbgInfo, NULL, "cpu_t", "cpu_kprunrun", &g_offrtSolCpuForceKernelPreempt);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "Failed to find cpu_t::cpu_kprunrun!\n");
+ goto errorbail;
+ }
+
+ rc = RTR0DbgKrnlInfoQueryMember(g_hKrnlDbgInfo, NULL, "kthread_t", "t_preempt", &g_offrtSolThreadPreempt);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "Failed to find kthread_t::t_preempt!\n");
+ goto errorbail;
+ }
+
+ rc = RTR0DbgKrnlInfoQueryMember(g_hKrnlDbgInfo, NULL, "kthread_t", "t_did", &g_offrtSolThreadId);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "Failed to find kthread_t::t_did!\n");
+ goto errorbail;
+ }
+
+ rc = RTR0DbgKrnlInfoQueryMember(g_hKrnlDbgInfo, NULL, "kthread_t", "t_intr", &g_offrtSolThreadIntrThread);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "Failed to find kthread_t::t_intr!\n");
+ goto errorbail;
+ }
+
+ rc = RTR0DbgKrnlInfoQueryMember(g_hKrnlDbgInfo, NULL, "kthread_t", "t_lockp", &g_offrtSolThreadLock);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "Failed to find kthread_t::t_lockp!\n");
+ goto errorbail;
+ }
+
+ rc = RTR0DbgKrnlInfoQueryMember(g_hKrnlDbgInfo, NULL, "kthread_t", "t_procp", &g_offrtSolThreadProc);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "Failed to find kthread_t::t_procp!\n");
+ goto errorbail;
+ }
+ cmn_err(CE_CONT, "!cpu_t::cpu_runrun @ 0x%lx (%ld)\n", g_offrtSolCpuPreempt, g_offrtSolCpuPreempt);
+ cmn_err(CE_CONT, "!cpu_t::cpu_kprunrun @ 0x%lx (%ld)\n", g_offrtSolCpuForceKernelPreempt, g_offrtSolCpuForceKernelPreempt);
+ cmn_err(CE_CONT, "!kthread_t::t_preempt @ 0x%lx (%ld)\n", g_offrtSolThreadPreempt, g_offrtSolThreadPreempt);
+ cmn_err(CE_CONT, "!kthread_t::t_did @ 0x%lx (%ld)\n", g_offrtSolThreadId, g_offrtSolThreadId);
+ cmn_err(CE_CONT, "!kthread_t::t_intr @ 0x%lx (%ld)\n", g_offrtSolThreadIntrThread, g_offrtSolThreadIntrThread);
+ cmn_err(CE_CONT, "!kthread_t::t_lockp @ 0x%lx (%ld)\n", g_offrtSolThreadLock, g_offrtSolThreadLock);
+ cmn_err(CE_CONT, "!kthread_t::t_procp @ 0x%lx (%ld)\n", g_offrtSolThreadProc, g_offrtSolThreadProc);
+
+ /*
+ * Mandatory: CPU cross call infrastructure. Refer the-solaris-kernel.h for details.
+ */
+ rc = RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /* pszModule */, "xc_init_cpu", NULL /* ppvSymbol */);
+ if (RT_SUCCESS(rc))
+ {
+ if (ncpus > IPRT_SOL_NCPUS)
+ {
+ cmn_err(CE_NOTE, "rtR0InitNative: CPU count mismatch! ncpus=%d IPRT_SOL_NCPUS=%d\n", ncpus, IPRT_SOL_NCPUS);
+ rc = VERR_NOT_SUPPORTED;
+ goto errorbail;
+ }
+ g_rtSolXcCall.u.pfnSol_xc_call = (void *)xc_call;
+ }
+ else
+ {
+ g_frtSolOldIPI = true;
+ g_rtSolXcCall.u.pfnSol_xc_call_old = (void *)xc_call;
+ if (max_cpuid + 1 == sizeof(ulong_t) * 8)
+ {
+ g_frtSolOldIPIUlong = true;
+ g_rtSolXcCall.u.pfnSol_xc_call_old_ulong = (void *)xc_call;
+ }
+ else if (max_cpuid + 1 != IPRT_SOL_NCPUS)
+ {
+ cmn_err(CE_NOTE, "rtR0InitNative: cpuset_t size mismatch! max_cpuid=%d IPRT_SOL_NCPUS=%d\n", max_cpuid,
+ IPRT_SOL_NCPUS);
+ rc = VERR_NOT_SUPPORTED;
+ goto errorbail;
+ }
+ }
+
+ /*
+ * Mandatory: Thread-context hooks.
+ */
+ rc = RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /* pszModule */, "exitctx", NULL /* ppvSymbol */);
+ if (RT_SUCCESS(rc))
+ {
+ g_rtSolThreadCtx.Install.pfnSol_installctx = (void *)installctx;
+ g_rtSolThreadCtx.Remove.pfnSol_removectx = (void *)removectx;
+ }
+ else
+ {
+ g_frtSolOldThreadCtx = true;
+ g_rtSolThreadCtx.Install.pfnSol_installctx_old = (void *)installctx;
+ g_rtSolThreadCtx.Remove.pfnSol_removectx_old = (void *)removectx;
+ }
+
+ /*
+ * Mandatory: map_addr() hooks.
+ */
+ rc = RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /* pszModule */, "plat_map_align_amount", NULL /* ppvSymbol */);
+ if (RT_SUCCESS(rc))
+ {
+ g_rtSolMapAddr.u.pfnSol_map_addr = (void *)map_addr;
+ }
+ else
+ {
+ g_frtSolOldMapAddr = true;
+ g_rtSolMapAddr.u.pfnSol_map_addr_old = (void *)map_addr;
+ }
+
+ /*
+ * Optional: Timeout hooks.
+ */
+ RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /* pszModule */, "timeout_generic",
+ (void **)&g_pfnrtR0Sol_timeout_generic);
+ RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /* pszModule */, "untimeout_generic",
+ (void **)&g_pfnrtR0Sol_untimeout_generic);
+ if ((g_pfnrtR0Sol_timeout_generic == NULL) != (g_pfnrtR0Sol_untimeout_generic == NULL))
+ {
+ static const char *s_apszFn[2] = { "timeout_generic", "untimeout_generic" };
+ bool iMissingFn = g_pfnrtR0Sol_timeout_generic == NULL;
+ cmn_err(CE_NOTE, "rtR0InitNative: Weird! Found %s but not %s!\n", s_apszFn[!iMissingFn], s_apszFn[iMissingFn]);
+ g_pfnrtR0Sol_timeout_generic = NULL;
+ g_pfnrtR0Sol_untimeout_generic = NULL;
+ }
+ RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /* pszModule */, "cyclic_reprogram",
+ (void **)&g_pfnrtR0Sol_cyclic_reprogram);
+
+ /*
+ * Optional: Querying page no-relocation support.
+ */
+ RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /*pszModule */, "page_noreloc_supported",
+ (void **)&g_pfnrtR0Sol_page_noreloc_supported);
+
+ /*
+ * Weak binding failures: contig_free
+ */
+ if (g_pfnrtR0Sol_contig_free == NULL)
+ {
+ rc = RTR0DbgKrnlInfoQuerySymbol(g_hKrnlDbgInfo, NULL /* pszModule */, "contig_free",
+ (void **)&g_pfnrtR0Sol_contig_free);
+ if (RT_FAILURE(rc))
+ {
+ cmn_err(CE_NOTE, "rtR0InitNative: failed to find contig_free!\n");
+ goto errorbail;
+ }
+ }
+
+ g_frtSolInitDone = true;
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ cmn_err(CE_NOTE, "RTR0DbgKrnlInfoOpen failed. rc=%d\n", rc);
+ return rc;
+ }
+
+errorbail:
+ RTR0DbgKrnlInfoRelease(g_hKrnlDbgInfo);
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtR0TermNative(void)
+{
+ RTR0DbgKrnlInfoRelease(g_hKrnlDbgInfo);
+ g_frtSolInitDone = false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.c
new file mode 100644
index 00000000..2308eff8
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.c
@@ -0,0 +1,1213 @@
+/* $Id: memobj-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/memobj.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include "internal/memobj.h"
+#include "memobj-r0drv-solaris.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define SOL_IS_KRNL_ADDR(vx) ((uintptr_t)(vx) >= kernelbase)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The Solaris version of the memory object structure.
+ */
+typedef struct RTR0MEMOBJSOL
+{
+ /** The core structure. */
+ RTR0MEMOBJINTERNAL Core;
+ /** Pointer to kernel memory cookie. */
+ ddi_umem_cookie_t Cookie;
+ /** Shadow locked pages. */
+ void *pvHandle;
+ /** Access during locking. */
+ int fAccess;
+ /** Set if large pages are involved in an RTR0MEMOBJTYPE_PHYS allocation. */
+ bool fLargePage;
+ /** Whether we have individual pages or a kernel-mapped virtual memory
+ * block in an RTR0MEMOBJTYPE_PHYS_NC allocation. */
+ bool fIndivPages;
+ /** Set if executable allocation - only RTR0MEMOBJTYPE_PHYS. */
+ bool fExecutable;
+} RTR0MEMOBJSOL, *PRTR0MEMOBJSOL;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static vnode_t g_PageVnode;
+static kmutex_t g_OffsetMtx;
+static u_offset_t g_offPage;
+
+static vnode_t g_LargePageVnode;
+static kmutex_t g_LargePageOffsetMtx;
+static u_offset_t g_offLargePage;
+static bool g_fLargePageNoReloc;
+
+
+/**
+ * Returns the physical address for a virtual address.
+ *
+ * @param pv The virtual address.
+ *
+ * @returns The physical address corresponding to @a pv.
+ */
+static uint64_t rtR0MemObjSolVirtToPhys(void *pv)
+{
+ struct hat *pHat = NULL;
+ pfn_t PageFrameNum = 0;
+ uintptr_t uVirtAddr = (uintptr_t)pv;
+
+ if (SOL_IS_KRNL_ADDR(pv))
+ pHat = kas.a_hat;
+ else
+ {
+ proc_t *pProcess = (proc_t *)RTR0ProcHandleSelf();
+ AssertRelease(pProcess);
+ pHat = pProcess->p_as->a_hat;
+ }
+
+ PageFrameNum = hat_getpfnum(pHat, (caddr_t)(uVirtAddr & PAGEMASK));
+ AssertReleaseMsg(PageFrameNum != PFN_INVALID, ("rtR0MemObjSolVirtToPhys failed. pv=%p\n", pv));
+ return (((uint64_t)PageFrameNum << PAGE_SHIFT) | (uVirtAddr & PAGE_OFFSET_MASK));
+}
+
+
+/**
+ * Returns the physical address for a page.
+ *
+ * @param pPage Pointer to the page.
+ *
+ * @returns The physical address for a page.
+ */
+static inline uint64_t rtR0MemObjSolPagePhys(page_t *pPage)
+{
+ AssertPtr(pPage);
+ pfn_t PageFrameNum = page_pptonum(pPage);
+ AssertReleaseMsg(PageFrameNum != PFN_INVALID, ("rtR0MemObjSolPagePhys failed pPage=%p\n"));
+ return (uint64_t)PageFrameNum << PAGE_SHIFT;
+}
+
+
+/**
+ * Allocates one page.
+ *
+ * @param virtAddr The virtual address to which this page maybe mapped in
+ * the future.
+ *
+ * @returns Pointer to the allocated page, NULL on failure.
+ */
+static page_t *rtR0MemObjSolPageAlloc(caddr_t virtAddr)
+{
+ u_offset_t offPage;
+ seg_t KernelSeg;
+
+ /*
+ * 16777215 terabytes of total memory for all VMs or
+ * restart 8000 1GB VMs 2147483 times until wraparound!
+ */
+ mutex_enter(&g_OffsetMtx);
+ AssertCompileSize(u_offset_t, sizeof(uint64_t)); NOREF(RTASSERTVAR);
+ g_offPage = RT_ALIGN_64(g_offPage, PAGE_SIZE) + PAGE_SIZE;
+ offPage = g_offPage;
+ mutex_exit(&g_OffsetMtx);
+
+ KernelSeg.s_as = &kas;
+ page_t *pPage = page_create_va(&g_PageVnode, offPage, PAGE_SIZE, PG_WAIT | PG_NORELOC, &KernelSeg, virtAddr);
+ if (RT_LIKELY(pPage))
+ {
+ /*
+ * Lock this page into memory "long term" to prevent this page from being paged out
+ * when we drop the page lock temporarily (during free). Downgrade to a shared lock
+ * to prevent page relocation.
+ */
+ page_pp_lock(pPage, 0 /* COW */, 1 /* Kernel */);
+ page_io_unlock(pPage);
+ page_downgrade(pPage);
+ Assert(PAGE_LOCKED_SE(pPage, SE_SHARED));
+ }
+
+ return pPage;
+}
+
+
+/**
+ * Destroys an allocated page.
+ *
+ * @param pPage Pointer to the page to be destroyed.
+ * @remarks This function expects page in @c pPage to be shared locked.
+ */
+static void rtR0MemObjSolPageDestroy(page_t *pPage)
+{
+ /*
+ * We need to exclusive lock the pages before freeing them, if upgrading the shared lock to exclusive fails,
+ * drop the page lock and look it up from the hash. Record the page offset before we drop the page lock as
+ * we cannot touch any page_t members once the lock is dropped.
+ */
+ AssertPtr(pPage);
+ Assert(PAGE_LOCKED_SE(pPage, SE_SHARED));
+
+ u_offset_t offPage = pPage->p_offset;
+ int rc = page_tryupgrade(pPage);
+ if (!rc)
+ {
+ page_unlock(pPage);
+ page_t *pFoundPage = page_lookup(&g_PageVnode, offPage, SE_EXCL);
+
+ /*
+ * Since we allocated the pages as PG_NORELOC we should only get back the exact page always.
+ */
+ AssertReleaseMsg(pFoundPage == pPage, ("Page lookup failed %p:%llx returned %p, expected %p\n",
+ &g_PageVnode, offPage, pFoundPage, pPage));
+ }
+ Assert(PAGE_LOCKED_SE(pPage, SE_EXCL));
+ page_pp_unlock(pPage, 0 /* COW */, 1 /* Kernel */);
+ page_destroy(pPage, 0 /* move it to the free list */);
+}
+
+
+/* Currently not used on 32-bits, define it to shut up gcc. */
+#if HC_ARCH_BITS == 64
+/**
+ * Allocates physical, non-contiguous memory of pages.
+ *
+ * @param puPhys Where to store the physical address of first page. Optional,
+ * can be NULL.
+ * @param cb The size of the allocation.
+ *
+ * @return Array of allocated pages, NULL on failure.
+ */
+static page_t **rtR0MemObjSolPagesAlloc(uint64_t *puPhys, size_t cb)
+{
+ /*
+ * VM1:
+ * The page freelist and cachelist both hold pages that are not mapped into any address space.
+ * The cachelist is not really free pages but when memory is exhausted they'll be moved to the
+ * free lists, it's the total of the free+cache list that we see on the 'free' column in vmstat.
+ *
+ * VM2:
+ * @todo Document what happens behind the scenes in VM2 regarding the free and cachelist.
+ */
+
+ /*
+ * Non-pageable memory reservation request for _4K pages, don't sleep.
+ */
+ size_t cPages = (cb + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ int rc = page_resv(cPages, KM_NOSLEEP);
+ if (rc)
+ {
+ size_t cbPages = cPages * sizeof(page_t *);
+ page_t **ppPages = kmem_zalloc(cbPages, KM_SLEEP);
+ if (RT_LIKELY(ppPages))
+ {
+ /*
+ * Get pages from kseg, the 'virtAddr' here is only for colouring but unfortunately
+ * we don't yet have the 'virtAddr' to which this memory may be mapped.
+ */
+ caddr_t virtAddr = 0;
+ for (size_t i = 0; i < cPages; i++, virtAddr += PAGE_SIZE)
+ {
+ /*
+ * Get a page from the free list locked exclusively. The page will be named (hashed in)
+ * and we rely on it during free. The page we get will be shared locked to prevent the page
+ * from being relocated.
+ */
+ page_t *pPage = rtR0MemObjSolPageAlloc(virtAddr);
+ if (RT_UNLIKELY(!pPage))
+ {
+ /*
+ * No page found, release whatever pages we grabbed so far.
+ */
+ for (size_t k = 0; k < i; k++)
+ rtR0MemObjSolPageDestroy(ppPages[k]);
+ kmem_free(ppPages, cbPages);
+ page_unresv(cPages);
+ return NULL;
+ }
+
+ ppPages[i] = pPage;
+ }
+
+ if (puPhys)
+ *puPhys = rtR0MemObjSolPagePhys(ppPages[0]);
+ return ppPages;
+ }
+
+ page_unresv(cPages);
+ }
+
+ return NULL;
+}
+#endif /* HC_ARCH_BITS == 64 */
+
+
+/**
+ * Frees the allocates pages.
+ *
+ * @param ppPages Pointer to the page list.
+ * @param cbPages Size of the allocation.
+ */
+static void rtR0MemObjSolPagesFree(page_t **ppPages, size_t cb)
+{
+ size_t cPages = (cb + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ size_t cbPages = cPages * sizeof(page_t *);
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ rtR0MemObjSolPageDestroy(ppPages[iPage]);
+
+ kmem_free(ppPages, cbPages);
+ page_unresv(cPages);
+}
+
+
+/**
+ * Allocates one large page.
+ *
+ * @param puPhys Where to store the physical address of the allocated
+ * page. Optional, can be NULL.
+ * @param cbLargePage Size of the large page.
+ *
+ * @returns Pointer to a list of pages that cover the large page, NULL on
+ * failure.
+ */
+static page_t **rtR0MemObjSolLargePageAlloc(uint64_t *puPhys, size_t cbLargePage)
+{
+ /*
+ * Check PG_NORELOC support for large pages. Using this helps prevent _1G page
+ * fragementation on systems that support it.
+ */
+ static bool fPageNoRelocChecked = false;
+ if (fPageNoRelocChecked == false)
+ {
+ fPageNoRelocChecked = true;
+ g_fLargePageNoReloc = false;
+ if ( g_pfnrtR0Sol_page_noreloc_supported
+ && g_pfnrtR0Sol_page_noreloc_supported(cbLargePage))
+ {
+ g_fLargePageNoReloc = true;
+ }
+ }
+
+ /*
+ * Non-pageable memory reservation request for _4K pages, don't sleep.
+ */
+ size_t cPages = (cbLargePage + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ size_t cbPages = cPages * sizeof(page_t *);
+ u_offset_t offPage = 0;
+ int rc = page_resv(cPages, KM_NOSLEEP);
+ if (rc)
+ {
+ page_t **ppPages = kmem_zalloc(cbPages, KM_SLEEP);
+ if (RT_LIKELY(ppPages))
+ {
+ mutex_enter(&g_LargePageOffsetMtx);
+ AssertCompileSize(u_offset_t, sizeof(uint64_t)); NOREF(RTASSERTVAR);
+ g_offLargePage = RT_ALIGN_64(g_offLargePage, cbLargePage) + cbLargePage;
+ offPage = g_offLargePage;
+ mutex_exit(&g_LargePageOffsetMtx);
+
+ seg_t KernelSeg;
+ KernelSeg.s_as = &kas;
+ page_t *pRootPage = page_create_va_large(&g_LargePageVnode, offPage, cbLargePage,
+ PG_EXCL | (g_fLargePageNoReloc ? PG_NORELOC : 0), &KernelSeg,
+ 0 /* vaddr */,NULL /* locality group */);
+ if (pRootPage)
+ {
+ /*
+ * Split it into sub-pages, downgrade each page to a shared lock to prevent page relocation.
+ */
+ page_t *pPageList = pRootPage;
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ {
+ page_t *pPage = pPageList;
+ AssertPtr(pPage);
+ AssertMsg(page_pptonum(pPage) == iPage + page_pptonum(pRootPage),
+ ("%p:%lx %lx+%lx\n", pPage, page_pptonum(pPage), iPage, page_pptonum(pRootPage)));
+ AssertMsg(pPage->p_szc == pRootPage->p_szc, ("Size code mismatch %p %d %d\n", pPage,
+ (int)pPage->p_szc, (int)pRootPage->p_szc));
+
+ /*
+ * Lock the page into memory "long term". This prevents callers of page_try_demote_pages() (such as the
+ * pageout scanner) from demoting the large page into smaller pages while we temporarily release the
+ * exclusive lock (during free). We pass "0, 1" since we've already accounted for availrmem during
+ * page_resv().
+ */
+ page_pp_lock(pPage, 0 /* COW */, 1 /* Kernel */);
+
+ page_sub(&pPageList, pPage);
+ page_io_unlock(pPage);
+ page_downgrade(pPage);
+ Assert(PAGE_LOCKED_SE(pPage, SE_SHARED));
+
+ ppPages[iPage] = pPage;
+ }
+ Assert(pPageList == NULL);
+ Assert(ppPages[0] == pRootPage);
+
+ uint64_t uPhys = rtR0MemObjSolPagePhys(pRootPage);
+ AssertMsg(!(uPhys & (cbLargePage - 1)), ("%llx %zx\n", uPhys, cbLargePage));
+ if (puPhys)
+ *puPhys = uPhys;
+ return ppPages;
+ }
+
+ /*
+ * Don't restore offPrev in case of failure (race condition), we have plenty of offset space.
+ * The offset must be unique (for the same vnode) or we'll encounter panics on page_create_va_large().
+ */
+ kmem_free(ppPages, cbPages);
+ }
+
+ page_unresv(cPages);
+ }
+ return NULL;
+}
+
+
+/**
+ * Frees the large page.
+ *
+ * @param ppPages Pointer to the list of small pages that cover the
+ * large page.
+ * @param cbLargePage Size of the allocation (i.e. size of the large
+ * page).
+ */
+static void rtR0MemObjSolLargePageFree(page_t **ppPages, size_t cbLargePage)
+{
+ Assert(ppPages);
+ Assert(cbLargePage > PAGE_SIZE);
+
+ bool fDemoted = false;
+ size_t cPages = (cbLargePage + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ size_t cbPages = cPages * sizeof(page_t *);
+ page_t *pPageList = ppPages[0];
+
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ {
+ /*
+ * We need the pages exclusively locked, try upgrading the shared lock.
+ * If it fails, drop the shared page lock (cannot access any page_t members once this is done)
+ * and lookup the page from the page hash locking it exclusively.
+ */
+ page_t *pPage = ppPages[iPage];
+ u_offset_t offPage = pPage->p_offset;
+ int rc = page_tryupgrade(pPage);
+ if (!rc)
+ {
+ page_unlock(pPage);
+ page_t *pFoundPage = page_lookup(&g_LargePageVnode, offPage, SE_EXCL);
+ AssertRelease(pFoundPage);
+
+ if (g_fLargePageNoReloc)
+ {
+ /*
+ * This can only be guaranteed if PG_NORELOC is used while allocating the pages.
+ */
+ AssertReleaseMsg(pFoundPage == pPage,
+ ("lookup failed %p:%llu returned %p, expected %p\n", &g_LargePageVnode, offPage,
+ pFoundPage, pPage));
+ }
+
+ /*
+ * Check for page demotion (regardless of relocation). Some places in Solaris (e.g. VM1 page_retire())
+ * could possibly demote the large page to _4K pages between our call to page_unlock() and page_lookup().
+ */
+ if (page_get_pagecnt(pFoundPage->p_szc) == 1) /* Base size of only _4K associated with this page. */
+ fDemoted = true;
+ pPage = pFoundPage;
+ ppPages[iPage] = pFoundPage;
+ }
+ Assert(PAGE_LOCKED_SE(pPage, SE_EXCL));
+ page_pp_unlock(pPage, 0 /* COW */, 1 /* Kernel */);
+ }
+
+ if (fDemoted)
+ {
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ {
+ Assert(page_get_pagecnt(ppPages[iPage]->p_szc) == 1);
+ page_destroy(ppPages[iPage], 0 /* move it to the free list */);
+ }
+ }
+ else
+ {
+ /*
+ * Although we shred the adjacent pages in the linked list, page_destroy_pages works on
+ * adjacent pages via array increments. So this does indeed free all the pages.
+ */
+ AssertPtr(pPageList);
+ page_destroy_pages(pPageList);
+ }
+ kmem_free(ppPages, cbPages);
+ page_unresv(cPages);
+}
+
+
+/**
+ * Unmaps kernel/user-space mapped memory.
+ *
+ * @param pv Pointer to the mapped memory block.
+ * @param cb Size of the memory block.
+ */
+static void rtR0MemObjSolUnmap(void *pv, size_t cb)
+{
+ if (SOL_IS_KRNL_ADDR(pv))
+ {
+ hat_unload(kas.a_hat, pv, cb, HAT_UNLOAD | HAT_UNLOAD_UNLOCK);
+ vmem_free(heap_arena, pv, cb);
+ }
+ else
+ {
+ struct as *pAddrSpace = ((proc_t *)RTR0ProcHandleSelf())->p_as;
+ AssertPtr(pAddrSpace);
+ as_rangelock(pAddrSpace);
+ as_unmap(pAddrSpace, pv, cb);
+ as_rangeunlock(pAddrSpace);
+ }
+}
+
+
+/**
+ * Lock down memory mappings for a virtual address.
+ *
+ * @param pv Pointer to the memory to lock down.
+ * @param cb Size of the memory block.
+ * @param fAccess Page access rights (S_READ, S_WRITE, S_EXEC)
+ *
+ * @returns IPRT status code.
+ */
+static int rtR0MemObjSolLock(void *pv, size_t cb, int fPageAccess)
+{
+ /*
+ * Kernel memory mappings on x86/amd64 are always locked, only handle user-space memory.
+ */
+ if (!SOL_IS_KRNL_ADDR(pv))
+ {
+ proc_t *pProc = (proc_t *)RTR0ProcHandleSelf();
+ AssertPtr(pProc);
+ faultcode_t rc = as_fault(pProc->p_as->a_hat, pProc->p_as, (caddr_t)pv, cb, F_SOFTLOCK, fPageAccess);
+ if (rc)
+ {
+ LogRel(("rtR0MemObjSolLock failed for pv=%pv cb=%lx fPageAccess=%d rc=%d\n", pv, cb, fPageAccess, rc));
+ return VERR_LOCK_FAILED;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unlock memory mappings for a virtual address.
+ *
+ * @param pv Pointer to the locked memory.
+ * @param cb Size of the memory block.
+ * @param fPageAccess Page access rights (S_READ, S_WRITE, S_EXEC).
+ */
+static void rtR0MemObjSolUnlock(void *pv, size_t cb, int fPageAccess)
+{
+ if (!SOL_IS_KRNL_ADDR(pv))
+ {
+ proc_t *pProcess = (proc_t *)RTR0ProcHandleSelf();
+ AssertPtr(pProcess);
+ as_fault(pProcess->p_as->a_hat, pProcess->p_as, (caddr_t)pv, cb, F_SOFTUNLOCK, fPageAccess);
+ }
+}
+
+
+/**
+ * Maps a list of physical pages into user address space.
+ *
+ * @param pVirtAddr Where to store the virtual address of the mapping.
+ * @param fPageAccess Page access rights (PROT_READ, PROT_WRITE,
+ * PROT_EXEC)
+ * @param paPhysAddrs Array of physical addresses to pages.
+ * @param cb Size of memory being mapped.
+ *
+ * @returns IPRT status code.
+ */
+static int rtR0MemObjSolUserMap(caddr_t *pVirtAddr, unsigned fPageAccess, uint64_t *paPhysAddrs, size_t cb, size_t cbPageSize)
+{
+ struct as *pAddrSpace = ((proc_t *)RTR0ProcHandleSelf())->p_as;
+ int rc;
+ SEGVBOX_CRARGS Args;
+
+ Args.paPhysAddrs = paPhysAddrs;
+ Args.fPageAccess = fPageAccess;
+ Args.cbPageSize = cbPageSize;
+
+ as_rangelock(pAddrSpace);
+ if (g_frtSolOldMapAddr)
+ g_rtSolMapAddr.u.pfnSol_map_addr_old(pVirtAddr, cb, 0 /* offset */, 0 /* vacalign */, MAP_SHARED);
+ else
+ g_rtSolMapAddr.u.pfnSol_map_addr(pVirtAddr, cb, 0 /* offset */, MAP_SHARED);
+ if (*pVirtAddr != NULL)
+ rc = as_map(pAddrSpace, *pVirtAddr, cb, rtR0SegVBoxSolCreate, &Args);
+ else
+ rc = ENOMEM;
+ as_rangeunlock(pAddrSpace);
+
+ return RTErrConvertFromErrno(rc);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem)
+{
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)pMem;
+
+ switch (pMemSolaris->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_LOW:
+ rtR0SolMemFree(pMemSolaris->Core.pv, pMemSolaris->Core.cb);
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS:
+ if (pMemSolaris->Core.u.Phys.fAllocated)
+ {
+ if (pMemSolaris->fLargePage)
+ rtR0MemObjSolLargePageFree(pMemSolaris->pvHandle, pMemSolaris->Core.cb);
+ else
+ rtR0SolMemFree(pMemSolaris->Core.pv, pMemSolaris->Core.cb);
+ }
+ break;
+
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ if (pMemSolaris->fIndivPages)
+ rtR0MemObjSolPagesFree(pMemSolaris->pvHandle, pMemSolaris->Core.cb);
+ else
+ rtR0SolMemFree(pMemSolaris->Core.pv, pMemSolaris->Core.cb);
+ break;
+
+ case RTR0MEMOBJTYPE_PAGE:
+ if (!pMemSolaris->fExecutable)
+ ddi_umem_free(pMemSolaris->Cookie);
+ else
+ segkmem_free(heaptext_arena, pMemSolaris->Core.pv, pMemSolaris->Core.cb);
+ break;
+
+ case RTR0MEMOBJTYPE_LOCK:
+ rtR0MemObjSolUnlock(pMemSolaris->Core.pv, pMemSolaris->Core.cb, pMemSolaris->fAccess);
+ break;
+
+ case RTR0MEMOBJTYPE_MAPPING:
+ rtR0MemObjSolUnmap(pMemSolaris->Core.pv, pMemSolaris->Core.cb);
+ break;
+
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ if (pMemSolaris->Core.u.ResVirt.R0Process == NIL_RTR0PROCESS)
+ vmem_xfree(heap_arena, pMemSolaris->Core.pv, pMemSolaris->Core.cb);
+ else
+ AssertFailed();
+ break;
+
+ case RTR0MEMOBJTYPE_CONT: /* we don't use this type here. */
+ default:
+ AssertMsgFailed(("enmType=%d\n", pMemSolaris->Core.enmType));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ /* Create the object. */
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PAGE, NULL, cb, pszTag);
+ if (pMemSolaris)
+ {
+ void *pvMem;
+ if (!fExecutable)
+ {
+ pMemSolaris->Core.fFlags |= RTR0MEMOBJ_FLAGS_ZERO_AT_ALLOC;
+ pvMem = ddi_umem_alloc(cb, DDI_UMEM_SLEEP, &pMemSolaris->Cookie);
+ }
+ else
+ {
+ pMemSolaris->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /** @todo does segkmem_alloc zero the memory? */
+ pvMem = segkmem_alloc(heaptext_arena, cb, KM_SLEEP);
+ }
+ if (pvMem)
+ {
+ pMemSolaris->Core.pv = pvMem;
+ pMemSolaris->pvHandle = NULL;
+ pMemSolaris->fExecutable = fExecutable;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return VERR_NO_PAGE_MEMORY;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLarge(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, size_t cbLargePage, uint32_t fFlags,
+ const char *pszTag)
+{
+ return rtR0MemObjFallbackAllocLarge(ppMem, cb, cbLargePage, fFlags, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ AssertReturn(!fExecutable, VERR_NOT_SUPPORTED);
+
+ /* Create the object */
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_LOW, NULL, cb, pszTag);
+ if (pMemSolaris)
+ {
+ /* Allocate physically low page-aligned memory. */
+ uint64_t uPhysHi = _4G - 1;
+ void *pvMem = rtR0SolMemAlloc(uPhysHi, NULL /* puPhys */, cb, PAGE_SIZE, false /* fContig */);
+ if (pvMem)
+ {
+ pMemSolaris->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ pMemSolaris->Core.pv = pvMem;
+ pMemSolaris->pvHandle = NULL;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+ }
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return VERR_NO_LOW_MEMORY;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable, const char *pszTag)
+{
+ AssertReturn(!fExecutable, VERR_NOT_SUPPORTED);
+ return rtR0MemObjNativeAllocPhys(ppMem, cb, _4G - 1, PAGE_SIZE /* alignment */, pszTag);
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, const char *pszTag)
+{
+#if HC_ARCH_BITS == 64
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PHYS_NC, NULL, cb, pszTag);
+ if (pMemSolaris)
+ {
+ if (PhysHighest == NIL_RTHCPHYS)
+ {
+ uint64_t PhysAddr = UINT64_MAX;
+ void *pvPages = rtR0MemObjSolPagesAlloc(&PhysAddr, cb);
+ if (!pvPages)
+ {
+ LogRel(("rtR0MemObjNativeAllocPhysNC: rtR0MemObjSolPagesAlloc failed for cb=%u.\n", cb));
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return VERR_NO_MEMORY;
+ }
+ Assert(PhysAddr != UINT64_MAX);
+ Assert(!(PhysAddr & PAGE_OFFSET_MASK));
+
+ pMemSolaris->Core.pv = NULL;
+ pMemSolaris->pvHandle = pvPages;
+ pMemSolaris->fIndivPages = true;
+ }
+ else
+ {
+ /*
+ * If we must satisfy an upper limit constraint, it isn't feasible to grab individual pages.
+ * We fall back to using contig_alloc().
+ */
+ uint64_t PhysAddr = UINT64_MAX;
+ void *pvMem = rtR0SolMemAlloc(PhysHighest, &PhysAddr, cb, PAGE_SIZE, false /* fContig */);
+ if (!pvMem)
+ {
+ LogRel(("rtR0MemObjNativeAllocPhysNC: rtR0SolMemAlloc failed for cb=%u PhysHighest=%RHp.\n", cb, PhysHighest));
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return VERR_NO_MEMORY;
+ }
+ Assert(PhysAddr != UINT64_MAX);
+ Assert(!(PhysAddr & PAGE_OFFSET_MASK));
+
+ pMemSolaris->Core.pv = pvMem;
+ pMemSolaris->pvHandle = NULL;
+ pMemSolaris->fIndivPages = false;
+ }
+ pMemSolaris->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+
+#else /* 32 bit: */
+ return VERR_NOT_SUPPORTED; /* see the RTR0MemObjAllocPhysNC specs */
+#endif
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment,
+ const char *pszTag)
+{
+ AssertMsgReturn(PhysHighest >= 16 *_1M, ("PhysHigest=%RHp\n", PhysHighest), VERR_NOT_SUPPORTED);
+
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag);
+ if (RT_UNLIKELY(!pMemSolaris))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Allocating one large page gets special treatment.
+ */
+ static uint32_t s_cbLargePage = UINT32_MAX;
+ if (s_cbLargePage == UINT32_MAX)
+ {
+ if (page_num_pagesizes() > 1)
+ ASMAtomicWriteU32(&s_cbLargePage, page_get_pagesize(1)); /* Page-size code 1 maps to _2M on Solaris x86/amd64. */
+ else
+ ASMAtomicWriteU32(&s_cbLargePage, 0);
+ }
+
+ uint64_t PhysAddr;
+ if ( cb == s_cbLargePage
+ && cb == uAlignment
+ && PhysHighest == NIL_RTHCPHYS)
+ {
+ /*
+ * Allocate one large page (backed by physically contiguous memory).
+ */
+ void *pvPages = rtR0MemObjSolLargePageAlloc(&PhysAddr, cb);
+ if (RT_LIKELY(pvPages))
+ {
+ AssertMsg(!(PhysAddr & (cb - 1)), ("%RHp\n", PhysAddr));
+ pMemSolaris->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC; /*?*/
+ pMemSolaris->Core.pv = NULL;
+ pMemSolaris->Core.u.Phys.PhysBase = PhysAddr;
+ pMemSolaris->Core.u.Phys.fAllocated = true;
+ pMemSolaris->pvHandle = pvPages;
+ pMemSolaris->fLargePage = true;
+
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ /*
+ * Allocate physically contiguous memory aligned as specified.
+ */
+ AssertCompile(NIL_RTHCPHYS == UINT64_MAX); NOREF(RTASSERTVAR);
+ PhysAddr = PhysHighest;
+ void *pvMem = rtR0SolMemAlloc(PhysHighest, &PhysAddr, cb, uAlignment, true /* fContig */);
+ if (RT_LIKELY(pvMem))
+ {
+ Assert(!(PhysAddr & PAGE_OFFSET_MASK));
+ Assert(PhysAddr < PhysHighest);
+ Assert(PhysAddr + cb <= PhysHighest);
+
+ pMemSolaris->Core.fFlags |= RTR0MEMOBJ_FLAGS_UNINITIALIZED_AT_ALLOC;
+ pMemSolaris->Core.pv = pvMem;
+ pMemSolaris->Core.u.Phys.PhysBase = PhysAddr;
+ pMemSolaris->Core.u.Phys.fAllocated = true;
+ pMemSolaris->pvHandle = NULL;
+ pMemSolaris->fLargePage = false;
+
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+ }
+ }
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return VERR_NO_CONT_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy,
+ const char *pszTag)
+{
+ AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE, VERR_NOT_SUPPORTED);
+
+ /* Create the object. */
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_PHYS, NULL, cb, pszTag);
+ if (!pMemSolaris)
+ return VERR_NO_MEMORY;
+
+ /* There is no allocation here, it needs to be mapped somewhere first. */
+ pMemSolaris->Core.u.Phys.fAllocated = false;
+ pMemSolaris->Core.u.Phys.PhysBase = Phys;
+ pMemSolaris->Core.u.Phys.uCachePolicy = uCachePolicy;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ AssertReturn(R0Process == RTR0ProcHandleSelf(), VERR_INVALID_PARAMETER);
+ NOREF(fAccess);
+
+ /* Create the locking object */
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_LOCK,
+ (void *)R3Ptr, cb, pszTag);
+ if (!pMemSolaris)
+ return VERR_NO_MEMORY;
+
+ /* Lock down user pages. */
+ int fPageAccess = S_READ;
+ if (fAccess & RTMEM_PROT_WRITE)
+ fPageAccess = S_WRITE;
+ if (fAccess & RTMEM_PROT_EXEC)
+ fPageAccess = S_EXEC;
+ int rc = rtR0MemObjSolLock((void *)R3Ptr, cb, fPageAccess);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("rtR0MemObjNativeLockUser: rtR0MemObjSolLock failed rc=%d\n", rc));
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return rc;
+ }
+
+ /* Fill in the object attributes and return successfully. */
+ pMemSolaris->Core.u.Lock.R0Process = R0Process;
+ pMemSolaris->pvHandle = NULL;
+ pMemSolaris->fAccess = fPageAccess;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess, const char *pszTag)
+{
+ NOREF(fAccess);
+
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_LOCK, pv, cb, pszTag);
+ if (!pMemSolaris)
+ return VERR_NO_MEMORY;
+
+ /* Lock down kernel pages. */
+ int fPageAccess = S_READ;
+ if (fAccess & RTMEM_PROT_WRITE)
+ fPageAccess = S_WRITE;
+ if (fAccess & RTMEM_PROT_EXEC)
+ fPageAccess = S_EXEC;
+ int rc = rtR0MemObjSolLock(pv, cb, fPageAccess);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("rtR0MemObjNativeLockKernel: rtR0MemObjSolLock failed rc=%d\n", rc));
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return rc;
+ }
+
+ /* Fill in the object attributes and return successfully. */
+ pMemSolaris->Core.u.Lock.R0Process = NIL_RTR0PROCESS;
+ pMemSolaris->pvHandle = NULL;
+ pMemSolaris->fAccess = fPageAccess;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment,
+ const char *pszTag)
+{
+ PRTR0MEMOBJSOL pMemSolaris;
+
+ /*
+ * Use xalloc.
+ */
+ void *pv = vmem_xalloc(heap_arena, cb, uAlignment, 0 /* phase */, 0 /* nocross */,
+ NULL /* minaddr */, NULL /* maxaddr */, VM_SLEEP);
+ if (RT_UNLIKELY(!pv))
+ return VERR_NO_MEMORY;
+
+ /* Create the object. */
+ pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_RES_VIRT, pv, cb, pszTag);
+ if (!pMemSolaris)
+ {
+ LogRel(("rtR0MemObjNativeReserveKernel failed to alloc memory object.\n"));
+ vmem_xfree(heap_arena, pv, cb);
+ return VERR_NO_MEMORY;
+ }
+
+ pMemSolaris->Core.u.ResVirt.R0Process = NIL_RTR0PROCESS;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment,
+ RTR0PROCESS R0Process, const char *pszTag)
+{
+ RT_NOREF(ppMem, R3PtrFixed, cb, uAlignment, R0Process, pszTag);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment,
+ unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag)
+{
+ /* Fail if requested to do something we can't. */
+ AssertMsgReturn(pvFixed == (void *)-1, ("%p\n", pvFixed), VERR_NOT_SUPPORTED);
+ if (uAlignment > PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Use xalloc to get address space.
+ */
+ if (!cbSub)
+ cbSub = pMemToMap->cb;
+ void *pv = vmem_xalloc(heap_arena, cbSub, uAlignment, 0 /* phase */, 0 /* nocross */,
+ NULL /* minaddr */, NULL /* maxaddr */, VM_SLEEP);
+ if (RT_UNLIKELY(!pv))
+ return VERR_MAP_FAILED;
+
+ /*
+ * Load the pages from the other object into it.
+ */
+ uint32_t fAttr = HAT_UNORDERED_OK | HAT_MERGING_OK | HAT_LOADCACHING_OK | HAT_STORECACHING_OK;
+ if (fProt & RTMEM_PROT_READ)
+ fAttr |= PROT_READ;
+ if (fProt & RTMEM_PROT_EXEC)
+ fAttr |= PROT_EXEC;
+ if (fProt & RTMEM_PROT_WRITE)
+ fAttr |= PROT_WRITE;
+ fAttr |= HAT_NOSYNC;
+
+ int rc = VINF_SUCCESS;
+ size_t off = 0;
+ while (off < cbSub)
+ {
+ RTHCPHYS HCPhys = RTR0MemObjGetPagePhysAddr(pMemToMap, (offSub + off) >> PAGE_SHIFT);
+ AssertBreakStmt(HCPhys != NIL_RTHCPHYS, rc = VERR_INTERNAL_ERROR_2);
+ pfn_t pfn = HCPhys >> PAGE_SHIFT;
+ AssertBreakStmt(((RTHCPHYS)pfn << PAGE_SHIFT) == HCPhys, rc = VERR_INTERNAL_ERROR_3);
+
+ hat_devload(kas.a_hat, (uint8_t *)pv + off, PAGE_SIZE, pfn, fAttr, HAT_LOAD_LOCK);
+
+ /* Advance. */
+ off += PAGE_SIZE;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a memory object for the mapping.
+ */
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_MAPPING,
+ pv, cbSub, pszTag);
+ if (pMemSolaris)
+ {
+ pMemSolaris->Core.u.Mapping.R0Process = NIL_RTR0PROCESS;
+ *ppMem = &pMemSolaris->Core;
+ return VINF_SUCCESS;
+ }
+
+ LogRel(("rtR0MemObjNativeMapKernel failed to alloc memory object.\n"));
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (off)
+ hat_unload(kas.a_hat, pv, off, HAT_UNLOAD | HAT_UNLOAD_UNLOCK);
+ vmem_xfree(heap_arena, pv, cbSub);
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, PRTR0MEMOBJINTERNAL pMemToMap, RTR3PTR R3PtrFixed,
+ size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process, size_t offSub, size_t cbSub,
+ const char *pszTag)
+{
+ /*
+ * Fend off things we cannot do.
+ */
+ AssertMsgReturn(R3PtrFixed == (RTR3PTR)-1, ("%p\n", R3PtrFixed), VERR_NOT_SUPPORTED);
+ AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED);
+ if (uAlignment != PAGE_SIZE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Get parameters from the source object and offSub/cbSub.
+ */
+ PRTR0MEMOBJSOL pMemToMapSolaris = (PRTR0MEMOBJSOL)pMemToMap;
+ uint8_t *pb = pMemToMapSolaris->Core.pv ? (uint8_t *)pMemToMapSolaris->Core.pv + offSub : NULL;
+ size_t const cb = cbSub ? cbSub : pMemToMapSolaris->Core.cb;
+ size_t const cPages = cb >> PAGE_SHIFT;
+ Assert(!offSub || cbSub);
+ Assert(!(cb & PAGE_OFFSET_MASK));
+
+ /*
+ * Create the mapping object
+ */
+ PRTR0MEMOBJSOL pMemSolaris;
+ pMemSolaris = (PRTR0MEMOBJSOL)rtR0MemObjNew(sizeof(*pMemSolaris), RTR0MEMOBJTYPE_MAPPING, pb, cb, pszTag);
+ if (RT_UNLIKELY(!pMemSolaris))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Gather the physical page address of the pages to be mapped.
+ */
+ int rc = VINF_SUCCESS;
+ uint64_t *paPhysAddrs = kmem_zalloc(sizeof(uint64_t) * cPages, KM_SLEEP);
+ if (RT_LIKELY(paPhysAddrs))
+ {
+ if ( pMemToMapSolaris->Core.enmType == RTR0MEMOBJTYPE_PHYS_NC
+ && pMemToMapSolaris->fIndivPages)
+ {
+ /* Translate individual page_t to physical addresses. */
+ page_t **papPages = pMemToMapSolaris->pvHandle;
+ AssertPtr(papPages);
+ papPages += offSub >> PAGE_SHIFT;
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ paPhysAddrs[iPage] = rtR0MemObjSolPagePhys(papPages[iPage]);
+ }
+ else if ( pMemToMapSolaris->Core.enmType == RTR0MEMOBJTYPE_PHYS
+ && pMemToMapSolaris->fLargePage)
+ {
+ /* Split up the large page into page-sized chunks. */
+ RTHCPHYS Phys = pMemToMapSolaris->Core.u.Phys.PhysBase;
+ Phys += offSub;
+ for (size_t iPage = 0; iPage < cPages; iPage++, Phys += PAGE_SIZE)
+ paPhysAddrs[iPage] = Phys;
+ }
+ else
+ {
+ /* Have kernel mapping, just translate virtual to physical. */
+ AssertPtr(pb);
+ for (size_t iPage = 0; iPage < cPages; iPage++)
+ {
+ paPhysAddrs[iPage] = rtR0MemObjSolVirtToPhys(pb);
+ if (RT_UNLIKELY(paPhysAddrs[iPage] == -(uint64_t)1))
+ {
+ LogRel(("rtR0MemObjNativeMapUser: no page to map.\n"));
+ rc = VERR_MAP_FAILED;
+ break;
+ }
+ pb += PAGE_SIZE;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Perform the actual mapping.
+ */
+ unsigned fPageAccess = PROT_READ;
+ if (fProt & RTMEM_PROT_WRITE)
+ fPageAccess |= PROT_WRITE;
+ if (fProt & RTMEM_PROT_EXEC)
+ fPageAccess |= PROT_EXEC;
+
+ caddr_t UserAddr = NULL;
+ rc = rtR0MemObjSolUserMap(&UserAddr, fPageAccess, paPhysAddrs, cb, PAGE_SIZE);
+ if (RT_SUCCESS(rc))
+ {
+ pMemSolaris->Core.u.Mapping.R0Process = R0Process;
+ pMemSolaris->Core.pv = UserAddr;
+
+ *ppMem = &pMemSolaris->Core;
+ kmem_free(paPhysAddrs, sizeof(uint64_t) * cPages);
+ return VINF_SUCCESS;
+ }
+
+ LogRel(("rtR0MemObjNativeMapUser: rtR0MemObjSolUserMap failed rc=%d.\n", rc));
+ }
+
+ rc = VERR_MAP_FAILED;
+ kmem_free(paPhysAddrs, sizeof(uint64_t) * cPages);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ rtR0MemObjDelete(&pMemSolaris->Core);
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt)
+{
+ NOREF(pMem);
+ NOREF(offSub);
+ NOREF(cbSub);
+ NOREF(fProt);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage)
+{
+ PRTR0MEMOBJSOL pMemSolaris = (PRTR0MEMOBJSOL)pMem;
+
+ switch (pMemSolaris->Core.enmType)
+ {
+ case RTR0MEMOBJTYPE_PHYS_NC:
+ if ( pMemSolaris->Core.u.Phys.fAllocated
+ || !pMemSolaris->fIndivPages)
+ {
+ uint8_t *pb = (uint8_t *)pMemSolaris->Core.pv + ((size_t)iPage << PAGE_SHIFT);
+ return rtR0MemObjSolVirtToPhys(pb);
+ }
+ page_t **ppPages = pMemSolaris->pvHandle;
+ return rtR0MemObjSolPagePhys(ppPages[iPage]);
+
+ case RTR0MEMOBJTYPE_PAGE:
+ case RTR0MEMOBJTYPE_LOW:
+ case RTR0MEMOBJTYPE_LOCK:
+ {
+ uint8_t *pb = (uint8_t *)pMemSolaris->Core.pv + ((size_t)iPage << PAGE_SHIFT);
+ return rtR0MemObjSolVirtToPhys(pb);
+ }
+
+ /*
+ * Although mapping can be handled by rtR0MemObjSolVirtToPhys(offset) like the above case,
+ * request it from the parent so that we have a clear distinction between CONT/PHYS_NC.
+ */
+ case RTR0MEMOBJTYPE_MAPPING:
+ return rtR0MemObjNativeGetPagePhysAddr(pMemSolaris->Core.uRel.Child.pParent, iPage);
+
+ case RTR0MEMOBJTYPE_CONT:
+ case RTR0MEMOBJTYPE_PHYS:
+ AssertFailed(); /* handled by the caller */
+ case RTR0MEMOBJTYPE_RES_VIRT:
+ default:
+ return NIL_RTHCPHYS;
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.h b/src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.h
new file mode 100644
index 00000000..8dbb6e5e
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/memobj-r0drv-solaris.h
@@ -0,0 +1,336 @@
+/* $Id: memobj-r0drv-solaris.h $ */
+/** @file
+ * IPRT - Ring-0 Memory Objects - Segment driver, Solaris.
+ */
+
+/*
+ * 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
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r0drv_solaris_memobj_r0drv_solaris_h
+#define IPRT_INCLUDED_SRC_r0drv_solaris_memobj_r0drv_solaris_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "the-solaris-kernel.h"
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+typedef struct SEGVBOX_CRARGS
+{
+ uint64_t *paPhysAddrs;
+ size_t cbPageSize;
+ uint_t fPageAccess;
+} SEGVBOX_CRARGS;
+typedef SEGVBOX_CRARGS *PSEGVBOX_CRARGS;
+
+typedef struct SEGVBOX_DATA
+{
+ uint_t fPageAccess;
+ size_t cbPageSize;
+} SEGVBOX_DATA;
+typedef SEGVBOX_DATA *PSEGVBOX_DATA;
+
+static struct seg_ops s_SegVBoxOps;
+static vnode_t s_segVBoxVnode;
+
+
+DECLINLINE(int) rtR0SegVBoxSolCreate(seg_t *pSeg, void *pvArgs)
+{
+ struct as *pAddrSpace = pSeg->s_as;
+ PSEGVBOX_CRARGS pArgs = pvArgs;
+ PSEGVBOX_DATA pData = kmem_zalloc(sizeof(*pData), KM_SLEEP);
+
+ AssertPtr(pAddrSpace);
+ AssertPtr(pArgs);
+ AssertPtr(pData);
+
+ /*
+ * Currently we only map _4K pages but this segment driver can handle any size
+ * supported by the Solaris HAT layer.
+ */
+ size_t cbPageSize = pArgs->cbPageSize;
+ size_t uPageShift = 0;
+ switch (cbPageSize)
+ {
+ case _4K: uPageShift = 12; break;
+ case _2M: uPageShift = 21; break;
+ default: AssertReleaseMsgFailed(("Unsupported page size for mapping cbPageSize=%llx\n", cbPageSize)); break;
+ }
+
+ hat_map(pAddrSpace->a_hat, pSeg->s_base, pSeg->s_size, HAT_MAP);
+ pData->fPageAccess = pArgs->fPageAccess | PROT_USER;
+ pData->cbPageSize = cbPageSize;
+
+ pSeg->s_ops = &s_SegVBoxOps;
+ pSeg->s_data = pData;
+
+ /*
+ * Now load and lock down the mappings to the physical addresses.
+ */
+ caddr_t virtAddr = pSeg->s_base;
+ pgcnt_t cPages = (pSeg->s_size + cbPageSize - 1) >> uPageShift;
+ for (pgcnt_t iPage = 0; iPage < cPages; ++iPage, virtAddr += cbPageSize)
+ {
+ hat_devload(pAddrSpace->a_hat, virtAddr, cbPageSize, pArgs->paPhysAddrs[iPage] >> uPageShift,
+ pData->fPageAccess | HAT_UNORDERED_OK, HAT_LOAD_LOCK);
+ }
+
+ return 0;
+}
+
+
+static int rtR0SegVBoxSolDup(seg_t *pSrcSeg, seg_t *pDstSeg)
+{
+ /*
+ * Duplicate a segment and return the new segment in 'pDstSeg'.
+ */
+ PSEGVBOX_DATA pSrcData = pSrcSeg->s_data;
+ PSEGVBOX_DATA pDstData = kmem_zalloc(sizeof(*pDstData), KM_SLEEP);
+
+ AssertPtr(pDstData);
+ AssertPtr(pSrcData);
+
+ pDstData->fPageAccess = pSrcData->fPageAccess;
+ pDstData->cbPageSize = pSrcData->cbPageSize;
+ pDstSeg->s_ops = &s_SegVBoxOps;
+ pDstSeg->s_data = pDstData;
+
+ return 0;
+}
+
+
+static int rtR0SegVBoxSolUnmap(seg_t *pSeg, caddr_t virtAddr, size_t cb)
+{
+ PSEGVBOX_DATA pData = pSeg->s_data;
+
+ AssertRelease(pData);
+ AssertReleaseMsg(virtAddr >= pSeg->s_base, ("virtAddr=%p s_base=%p\n", virtAddr, pSeg->s_base));
+ AssertReleaseMsg(virtAddr + cb <= pSeg->s_base + pSeg->s_size, ("virtAddr=%p cb=%llu s_base=%p s_size=%llu\n", virtAddr,
+ cb, pSeg->s_base, pSeg->s_size));
+ size_t cbPageOffset = pData->cbPageSize - 1;
+ AssertRelease(!(cb & cbPageOffset));
+ AssertRelease(!((uintptr_t)virtAddr & cbPageOffset));
+
+ if ( virtAddr != pSeg->s_base
+ || cb != pSeg->s_size)
+ {
+ return ENOTSUP;
+ }
+
+ hat_unload(pSeg->s_as->a_hat, virtAddr, cb, HAT_UNLOAD_UNMAP | HAT_UNLOAD_UNLOCK);
+
+ seg_free(pSeg);
+ return 0;
+}
+
+
+static void rtR0SegVBoxSolFree(seg_t *pSeg)
+{
+ PSEGVBOX_DATA pData = pSeg->s_data;
+ kmem_free(pData, sizeof(*pData));
+}
+
+
+static int rtR0SegVBoxSolFault(struct hat *pHat, seg_t *pSeg, caddr_t virtAddr, size_t cb, enum fault_type FaultType,
+ enum seg_rw ReadWrite)
+{
+ /*
+ * We would demand fault if the (u)read() path would SEGOP_FAULT() on buffers mapped in via our
+ * segment driver i.e. prefaults before DMA. Don't fail in such case where we're called directly,
+ * see @bugref{5047}.
+ */
+ return 0;
+}
+
+
+static int rtR0SegVBoxSolFaultA(seg_t *pSeg, caddr_t virtAddr)
+{
+ return 0;
+}
+
+
+static int rtR0SegVBoxSolSetProt(seg_t *pSeg, caddr_t virtAddr, size_t cb, uint_t fPageAccess)
+{
+ return EACCES;
+}
+
+
+static int rtR0SegVBoxSolCheckProt(seg_t *pSeg, caddr_t virtAddr, size_t cb, uint_t fPageAccess)
+{
+ return EINVAL;
+}
+
+
+static int rtR0SegVBoxSolKluster(seg_t *pSeg, caddr_t virtAddr, ssize_t Delta)
+{
+ return -1;
+}
+
+
+static int rtR0SegVBoxSolSync(seg_t *pSeg, caddr_t virtAddr, size_t cb, int Attr, uint_t fFlags)
+{
+ return 0;
+}
+
+
+static size_t rtR0SegVBoxSolInCore(seg_t *pSeg, caddr_t virtAddr, size_t cb, char *pVec)
+{
+ PSEGVBOX_DATA pData = pSeg->s_data;
+ AssertRelease(pData);
+ size_t uPageOffset = pData->cbPageSize - 1;
+ size_t uPageMask = ~uPageOffset;
+ size_t cbLen = (cb + uPageOffset) & uPageMask;
+ for (virtAddr = 0; cbLen != 0; cbLen -= pData->cbPageSize, virtAddr += pData->cbPageSize)
+ *pVec++ = 1;
+ return cbLen;
+}
+
+
+static int rtR0SegVBoxSolLockOp(seg_t *pSeg, caddr_t virtAddr, size_t cb, int Attr, int Op, ulong_t *pLockMap, size_t off)
+{
+ return 0;
+}
+
+
+static int rtR0SegVBoxSolGetProt(seg_t *pSeg, caddr_t virtAddr, size_t cb, uint_t *pafPageAccess)
+{
+ PSEGVBOX_DATA pData = pSeg->s_data;
+ size_t iPage = seg_page(pSeg, virtAddr + cb) - seg_page(pSeg, virtAddr) + 1;
+ if (iPage)
+ {
+ do
+ {
+ iPage--;
+ pafPageAccess[iPage] = pData->fPageAccess;
+ } while (iPage);
+ }
+ return 0;
+}
+
+
+static u_offset_t rtR0SegVBoxSolGetOffset(seg_t *pSeg, caddr_t virtAddr)
+{
+ return ((uintptr_t)virtAddr - (uintptr_t)pSeg->s_base);
+}
+
+
+static int rtR0SegVBoxSolGetType(seg_t *pSeg, caddr_t virtAddr)
+{
+ return MAP_SHARED;
+}
+
+
+static int rtR0SegVBoxSolGetVp(seg_t *pSeg, caddr_t virtAddr, vnode_t **ppVnode)
+{
+ *ppVnode = &s_segVBoxVnode;
+ return 0;
+}
+
+
+static int rtR0SegVBoxSolAdvise(seg_t *pSeg, caddr_t virtAddr, size_t cb, uint_t Behav /* wut? */)
+{
+ return 0;
+}
+
+
+#if defined(VBOX_NEW_CRASH_DUMP_FORMAT)
+static void rtR0SegVBoxSolDump(seg_t *pSeg, dump_addpage_f Func)
+#else
+static void rtR0SegVBoxSolDump(seg_t *pSeg)
+#endif
+{
+ /* Nothing to do. */
+}
+
+
+static int rtR0SegVBoxSolPageLock(seg_t *pSeg, caddr_t virtAddr, size_t cb, page_t ***pppPage, enum lock_type LockType, enum seg_rw ReadWrite)
+{
+ return ENOTSUP;
+}
+
+
+static int rtR0SegVBoxSolSetPageSize(seg_t *pSeg, caddr_t virtAddr, size_t cb, uint_t SizeCode)
+{
+ return ENOTSUP;
+}
+
+
+static int rtR0SegVBoxSolGetMemId(seg_t *pSeg, caddr_t virtAddr, memid_t *pMemId)
+{
+ return ENODEV;
+}
+
+
+#ifdef SEGOP_CAPABLE
+static int rtR0SegVBoxSolCapable(seg_t *pSeg, segcapability_t Capab)
+{
+ return 0;
+}
+#endif
+
+
+static struct seg_ops s_SegVBoxOps =
+{
+ rtR0SegVBoxSolDup,
+ rtR0SegVBoxSolUnmap,
+ rtR0SegVBoxSolFree,
+ rtR0SegVBoxSolFault,
+ rtR0SegVBoxSolFaultA,
+ rtR0SegVBoxSolSetProt,
+ rtR0SegVBoxSolCheckProt,
+ rtR0SegVBoxSolKluster,
+ NULL, /* swapout */
+ rtR0SegVBoxSolSync,
+ rtR0SegVBoxSolInCore,
+ rtR0SegVBoxSolLockOp,
+ rtR0SegVBoxSolGetProt,
+ rtR0SegVBoxSolGetOffset,
+ rtR0SegVBoxSolGetType,
+ rtR0SegVBoxSolGetVp,
+ rtR0SegVBoxSolAdvise,
+ rtR0SegVBoxSolDump,
+ rtR0SegVBoxSolPageLock,
+ rtR0SegVBoxSolSetPageSize,
+ rtR0SegVBoxSolGetMemId,
+ NULL, /* getpolicy() */
+#ifdef SEGOP_CAPABLE
+ rtR0SegVBoxSolCapable
+#endif
+};
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_solaris_memobj_r0drv_solaris_h */
+
diff --git a/src/VBox/Runtime/r0drv/solaris/memuserkernel-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/memuserkernel-r0drv-solaris.c
new file mode 100644
index 00000000..81278fd7
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/memuserkernel-r0drv-solaris.c
@@ -0,0 +1,110 @@
+/* $Id: memuserkernel-r0drv-solaris.c $ */
+/** @file
+ * IPRT - User & Kernel Memory, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+
+
+RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb)
+{
+ int rc;
+ RT_ASSERT_INTS_ON();
+
+ rc = ddi_copyin((const char *)R3PtrSrc, pvDst, cb, 0 /*flags*/);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb)
+{
+ int rc;
+ RT_ASSERT_INTS_ON();
+
+ rc = ddi_copyout(pvSrc, (void *)R3PtrDst, cb, 0 /*flags*/);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr)
+{
+ return R3Ptr < kernelbase;
+}
+
+
+RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv)
+{
+ return (uintptr_t)pv >= kernelbase;
+}
+
+
+RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void)
+{
+ return true;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb)
+{
+ int rc = kcopy(pvSrc, pvDst, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
+
+RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb)
+{
+ int rc = kcopy(pvSrc, pvDst, cb);
+ if (RT_LIKELY(rc == 0))
+ return VINF_SUCCESS;
+ return VERR_ACCESS_DENIED;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/modulestub-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/modulestub-r0drv-solaris.c
new file mode 100644
index 00000000..0e053c47
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/modulestub-r0drv-solaris.c
@@ -0,0 +1,89 @@
+/* $Id: modulestub-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Ring-0 Solaris stubs
+ */
+
+/*
+ * 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 <sys/modctl.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static struct modlmisc g_rtModuleStubMisc =
+{
+ &mod_miscops, /* extern from kernel */
+ "platform agnostic module"
+};
+
+
+static struct modlinkage g_rtModuleStubModLinkage =
+{
+ MODREV_1, /* loadable module system revision */
+ {
+ &g_rtModuleStubMisc,
+ NULL /* terminate array of linkage structures */
+ }
+};
+
+
+
+int _init(void);
+int _init(void)
+{
+ /* Disable auto unloading. */
+ modctl_t *pModCtl = mod_getctl(&g_rtModuleStubModLinkage);
+ if (pModCtl)
+ pModCtl->mod_loadflags |= MOD_NOAUTOUNLOAD;
+
+ return mod_install(&g_rtModuleStubModLinkage);
+}
+
+
+int _fini(void);
+int _fini(void)
+{
+ return mod_remove(&g_rtModuleStubModLinkage);
+}
+
+
+int _info(struct modinfo *pModInfo);
+int _info(struct modinfo *pModInfo)
+{
+ return mod_info(&g_rtModuleStubModLinkage, pModInfo);
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/mp-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/mp-r0drv-solaris.c
new file mode 100644
index 00000000..0f4f63e5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/mp-r0drv-solaris.c
@@ -0,0 +1,460 @@
+/* $Id: mp-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Multiprocessor, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/thread.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/err.h>
+#include "r0drv/mp-r0drv.h"
+
+typedef int FNRTMPSOLWORKER(void *pvUser1, void *pvUser2, void *pvUser3);
+typedef FNRTMPSOLWORKER *PFNRTMPSOLWORKER;
+
+
+RTDECL(bool) RTMpIsCpuWorkPending(void)
+{
+ return false;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ return CPU->cpu_id;
+}
+
+
+RTDECL(int) RTMpCurSetIndex(void)
+{
+ return CPU->cpu_id;
+}
+
+
+RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu)
+{
+ return *pidCpu = CPU->cpu_id;
+}
+
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ return idCpu < RTCPUSET_MAX_CPUS && idCpu <= max_cpuid ? idCpu : -1;
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ return (unsigned)iCpu <= max_cpuid ? iCpu : NIL_RTCPUID;
+}
+
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ return max_cpuid;
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ /*
+ * We cannot query CPU status recursively, check cpu member from cached set.
+ */
+ if (idCpu >= ncpus)
+ return false;
+
+ return RTCpuSetIsMember(&g_rtMpSolCpuSet, idCpu);
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ return idCpu < ncpus;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID idCpu;
+
+ RTCpuSetEmpty(pSet);
+ idCpu = RTMpGetMaxCpuId(); /* it's inclusive */
+ do
+ {
+ if (RTMpIsCpuPossible(idCpu))
+ RTCpuSetAdd(pSet, idCpu);
+ } while (idCpu-- > 0);
+
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ return ncpus;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ /*
+ * We cannot query CPU status recursively, return the cached set.
+ */
+ *pSet = g_rtMpSolCpuSet;
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
+
+/**
+ * Wrapper to Solaris IPI infrastructure.
+ *
+ * @returns Solaris error code.
+ * @param pCpuSet Pointer to Solaris CPU set.
+ * @param pfnSolWorker Function to execute on target CPU(s).
+ * @param pArgs Pointer to RTMPARGS to pass to @a pfnSolWorker.
+ */
+static void rtMpSolCrossCall(PRTSOLCPUSET pCpuSet, PFNRTMPSOLWORKER pfnSolWorker, PRTMPARGS pArgs)
+{
+ AssertPtrReturnVoid(pCpuSet);
+ AssertPtrReturnVoid(pfnSolWorker);
+ AssertPtrReturnVoid(pCpuSet);
+
+ if (g_frtSolOldIPI)
+ {
+ if (g_frtSolOldIPIUlong)
+ {
+ g_rtSolXcCall.u.pfnSol_xc_call_old_ulong((xc_arg_t)pArgs, /* Arg to IPI function */
+ 0, /* Arg2, ignored */
+ 0, /* Arg3, ignored */
+ IPRT_SOL_X_CALL_HIPRI, /* IPI priority */
+ pCpuSet->auCpus[0], /* Target CPU(s) */
+ (xc_func_t)pfnSolWorker); /* Function to execute on target(s) */
+ }
+ else
+ {
+ g_rtSolXcCall.u.pfnSol_xc_call_old((xc_arg_t)pArgs, /* Arg to IPI function */
+ 0, /* Arg2, ignored */
+ 0, /* Arg3, ignored */
+ IPRT_SOL_X_CALL_HIPRI, /* IPI priority */
+ *pCpuSet, /* Target CPU set */
+ (xc_func_t)pfnSolWorker); /* Function to execute on target(s) */
+ }
+ }
+ else
+ {
+ g_rtSolXcCall.u.pfnSol_xc_call((xc_arg_t)pArgs, /* Arg to IPI function */
+ 0, /* Arg2 */
+ 0, /* Arg3 */
+ &pCpuSet->auCpus[0], /* Target CPU set */
+ (xc_func_t)pfnSolWorker); /* Function to execute on target(s) */
+ }
+}
+
+
+/**
+ * Wrapper between the native solaris per-cpu callback and PFNRTWORKER
+ * for the RTMpOnAll API.
+ *
+ * @returns Solaris error code.
+ * @param uArgs Pointer to the RTMPARGS package.
+ * @param pvIgnored1 Ignored.
+ * @param pvIgnored2 Ignored.
+ */
+static int rtMpSolOnAllCpuWrapper(void *uArg, void *pvIgnored1, void *pvIgnored2)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)(uArg);
+
+ /*
+ * Solaris CPU cross calls execute on offline CPUs too. Check our CPU cache
+ * set and ignore if it's offline.
+ */
+ if (!RTMpIsCpuOnline(RTMpCpuId()))
+ return 0;
+
+ pArgs->pfnWorker(RTMpCpuId(), pArgs->pvUser1, pArgs->pvUser2);
+
+ NOREF(pvIgnored1);
+ NOREF(pvIgnored2);
+ return 0;
+}
+
+
+RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RTMPARGS Args;
+ RTSOLCPUSET CpuSet;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ RT_ASSERT_INTS_ON();
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = NIL_RTCPUID;
+ Args.cHits = 0;
+
+ for (int i = 0; i < IPRT_SOL_SET_WORDS; i++)
+ CpuSet.auCpus[i] = (ulong_t)-1L;
+
+ RTThreadPreemptDisable(&PreemptState);
+
+ rtMpSolCrossCall(&CpuSet, rtMpSolOnAllCpuWrapper, &Args);
+
+ RTThreadPreemptRestore(&PreemptState);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Wrapper between the native solaris per-cpu callback and PFNRTWORKER
+ * for the RTMpOnOthers API.
+ *
+ * @returns Solaris error code.
+ * @param uArgs Pointer to the RTMPARGS package.
+ * @param pvIgnored1 Ignored.
+ * @param pvIgnored2 Ignored.
+ */
+static int rtMpSolOnOtherCpusWrapper(void *uArg, void *pvIgnored1, void *pvIgnored2)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)(uArg);
+ RTCPUID idCpu = RTMpCpuId();
+
+ Assert(idCpu != pArgs->idCpu);
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+
+ NOREF(pvIgnored1);
+ NOREF(pvIgnored2);
+ return 0;
+}
+
+
+RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RTMPARGS Args;
+ RTSOLCPUSET CpuSet;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ RT_ASSERT_INTS_ON();
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = RTMpCpuId();
+ Args.cHits = 0;
+
+ /* The caller is supposed to have disabled preemption, but take no chances. */
+ RTThreadPreemptDisable(&PreemptState);
+
+ for (int i = 0; i < IPRT_SOL_SET_WORDS; i++)
+ CpuSet.auCpus[0] = (ulong_t)-1L;
+ BT_CLEAR(CpuSet.auCpus, RTMpCpuId());
+
+ rtMpSolCrossCall(&CpuSet, rtMpSolOnOtherCpusWrapper, &Args);
+
+ RTThreadPreemptRestore(&PreemptState);
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Wrapper between the native solaris per-cpu callback and PFNRTWORKER
+ * for the RTMpOnPair API.
+ *
+ * @returns Solaris error code.
+ * @param uArgs Pointer to the RTMPARGS package.
+ * @param pvIgnored1 Ignored.
+ * @param pvIgnored2 Ignored.
+ */
+static int rtMpSolOnPairCpuWrapper(void *uArg, void *pvIgnored1, void *pvIgnored2)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)(uArg);
+ RTCPUID idCpu = RTMpCpuId();
+
+ Assert(idCpu == pArgs->idCpu || idCpu == pArgs->idCpu2);
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+
+ NOREF(pvIgnored1);
+ NOREF(pvIgnored2);
+ return 0;
+}
+
+
+RTDECL(int) RTMpOnPair(RTCPUID idCpu1, RTCPUID idCpu2, uint32_t fFlags, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ int rc;
+ RTMPARGS Args;
+ RTSOLCPUSET CpuSet;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+
+ AssertReturn(idCpu1 != idCpu2, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & RTMPON_F_VALID_MASK), VERR_INVALID_FLAGS);
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu1;
+ Args.idCpu2 = idCpu2;
+ Args.cHits = 0;
+
+ for (int i = 0; i < IPRT_SOL_SET_WORDS; i++)
+ CpuSet.auCpus[i] = 0;
+ BT_SET(CpuSet.auCpus, idCpu1);
+ BT_SET(CpuSet.auCpus, idCpu2);
+
+ /*
+ * Check that both CPUs are online before doing the broadcast call.
+ */
+ RTThreadPreemptDisable(&PreemptState);
+ if ( RTMpIsCpuOnline(idCpu1)
+ && RTMpIsCpuOnline(idCpu2))
+ {
+ rtMpSolCrossCall(&CpuSet, rtMpSolOnPairCpuWrapper, &Args);
+
+ Assert(Args.cHits <= 2);
+ if (Args.cHits == 2)
+ rc = VINF_SUCCESS;
+ else if (Args.cHits == 1)
+ rc = VERR_NOT_ALL_CPUS_SHOWED;
+ else if (Args.cHits == 0)
+ rc = VERR_CPU_OFFLINE;
+ else
+ rc = VERR_CPU_IPE_1;
+ }
+ /*
+ * A CPU must be present to be considered just offline.
+ */
+ else if ( RTMpIsCpuPresent(idCpu1)
+ && RTMpIsCpuPresent(idCpu2))
+ rc = VERR_CPU_OFFLINE;
+ else
+ rc = VERR_CPU_NOT_FOUND;
+
+ RTThreadPreemptRestore(&PreemptState);
+ return rc;
+}
+
+
+RTDECL(bool) RTMpOnPairIsConcurrentExecSupported(void)
+{
+ return true;
+}
+
+
+/**
+ * Wrapper between the native solaris per-cpu callback and PFNRTWORKER
+ * for the RTMpOnSpecific API.
+ *
+ * @returns Solaris error code.
+ * @param uArgs Pointer to the RTMPARGS package.
+ * @param pvIgnored1 Ignored.
+ * @param pvIgnored2 Ignored.
+ */
+static int rtMpSolOnSpecificCpuWrapper(void *uArg, void *pvIgnored1, void *pvIgnored2)
+{
+ PRTMPARGS pArgs = (PRTMPARGS)(uArg);
+ RTCPUID idCpu = RTMpCpuId();
+
+ Assert(idCpu == pArgs->idCpu);
+ pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2);
+ ASMAtomicIncU32(&pArgs->cHits);
+
+ NOREF(pvIgnored1);
+ NOREF(pvIgnored2);
+ return 0;
+}
+
+
+RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2)
+{
+ RTMPARGS Args;
+ RTSOLCPUSET CpuSet;
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ RT_ASSERT_INTS_ON();
+
+ if (idCpu >= ncpus)
+ return VERR_CPU_NOT_FOUND;
+
+ if (RT_UNLIKELY(!RTMpIsCpuOnline(idCpu)))
+ return RTMpIsCpuPresent(idCpu) ? VERR_CPU_OFFLINE : VERR_CPU_NOT_FOUND;
+
+ Args.pfnWorker = pfnWorker;
+ Args.pvUser1 = pvUser1;
+ Args.pvUser2 = pvUser2;
+ Args.idCpu = idCpu;
+ Args.cHits = 0;
+
+ for (int i = 0; i < IPRT_SOL_SET_WORDS; i++)
+ CpuSet.auCpus[i] = 0;
+ BT_SET(CpuSet.auCpus, idCpu);
+
+ RTThreadPreemptDisable(&PreemptState);
+
+ rtMpSolCrossCall(&CpuSet, rtMpSolOnSpecificCpuWrapper, &Args);
+
+ RTThreadPreemptRestore(&PreemptState);
+
+ Assert(ASMAtomicUoReadU32(&Args.cHits) <= 1);
+
+ return ASMAtomicUoReadU32(&Args.cHits) == 1
+ ? VINF_SUCCESS
+ : VERR_CPU_NOT_FOUND;
+}
+
+
+RTDECL(bool) RTMpOnAllIsConcurrentSafe(void)
+{
+ return true;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/mpnotification-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/mpnotification-r0drv-solaris.c
new file mode 100644
index 00000000..983b5790
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/mpnotification-r0drv-solaris.c
@@ -0,0 +1,149 @@
+/* $Id: mpnotification-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Multiprocessor Event Notifications, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/mp.h>
+#include <iprt/cpuset.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "r0drv/mp-r0drv.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether CPUs are being watched or not. */
+static volatile bool g_fSolCpuWatch = false;
+/** Set of online cpus that is maintained by the MP callback.
+ * This avoids locking issues querying the set from the kernel as well as
+ * eliminating any uncertainty regarding the online status during the
+ * callback. */
+RTCPUSET g_rtMpSolCpuSet;
+
+/**
+ * Internal solaris representation for watching CPUs.
+ */
+typedef struct RTMPSOLWATCHCPUS
+{
+ /** Function pointer to Mp worker. */
+ PFNRTMPWORKER pfnWorker;
+ /** Argument to pass to the Mp worker. */
+ void *pvArg;
+} RTMPSOLWATCHCPUS;
+typedef RTMPSOLWATCHCPUS *PRTMPSOLWATCHCPUS;
+
+
+/**
+ * Solaris callback function for Mp event notification.
+ *
+ * @returns Solaris error code.
+ * @param CpuState The current event/state of the CPU.
+ * @param iCpu Which CPU is this event for.
+ * @param pvArg Ignored.
+ *
+ * @remarks This function assumes index == RTCPUID.
+ * We may -not- be firing on the CPU going online/offline and called
+ * with preemption enabled.
+ */
+static int rtMpNotificationCpuEvent(cpu_setup_t CpuState, int iCpu, void *pvArg)
+{
+ RTMPEVENT enmMpEvent;
+
+ /*
+ * Update our CPU set structures first regardless of whether we've been
+ * scheduled on the right CPU or not, this is just atomic accounting.
+ */
+ if (CpuState == CPU_ON)
+ {
+ enmMpEvent = RTMPEVENT_ONLINE;
+ RTCpuSetAdd(&g_rtMpSolCpuSet, iCpu);
+ }
+ else if (CpuState == CPU_OFF)
+ {
+ enmMpEvent = RTMPEVENT_OFFLINE;
+ RTCpuSetDel(&g_rtMpSolCpuSet, iCpu);
+ }
+ else
+ return 0;
+
+ rtMpNotificationDoCallbacks(enmMpEvent, iCpu);
+ NOREF(pvArg);
+ return 0;
+}
+
+
+DECLHIDDEN(int) rtR0MpNotificationNativeInit(void)
+{
+ if (ASMAtomicReadBool(&g_fSolCpuWatch) == true)
+ return VERR_WRONG_ORDER;
+
+ /*
+ * Register the callback building the online cpu set as we do so.
+ */
+ RTCpuSetEmpty(&g_rtMpSolCpuSet);
+
+ mutex_enter(&cpu_lock);
+ register_cpu_setup_func(rtMpNotificationCpuEvent, NULL /* pvArg */);
+
+ for (int i = 0; i < (int)RTMpGetCount(); ++i)
+ if (cpu_is_online(cpu[i]))
+ rtMpNotificationCpuEvent(CPU_ON, i, NULL /* pvArg */);
+
+ ASMAtomicWriteBool(&g_fSolCpuWatch, true);
+ mutex_exit(&cpu_lock);
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtR0MpNotificationNativeTerm(void)
+{
+ if (ASMAtomicReadBool(&g_fSolCpuWatch) == true)
+ {
+ mutex_enter(&cpu_lock);
+ unregister_cpu_setup_func(rtMpNotificationCpuEvent, NULL /* pvArg */);
+ ASMAtomicWriteBool(&g_fSolCpuWatch, false);
+ mutex_exit(&cpu_lock);
+ }
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/process-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/process-r0drv-solaris.c
new file mode 100644
index 00000000..0d9be4d9
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/process-r0drv-solaris.c
@@ -0,0 +1,59 @@
+/* $Id: process-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Process Management, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/process.h>
+
+
+
+RTDECL(RTPROCESS) RTProcSelf(void)
+{
+ return ddi_get_pid();
+}
+
+
+RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void)
+{
+ proc_t *pProcess = NULL;
+ drv_getparm(UPROCP, &pProcess);
+ return (RTR0PROCESS)pProcess;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c
new file mode 100644
index 00000000..0fab932b
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/semevent-r0drv-solaris.c
@@ -0,0 +1,364 @@
+/* $Id: semevent-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Single Release Event Semaphores, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 RTSEMEVENT_WITHOUT_REMAPPING
+#include "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/err.h>
+#include <iprt/list.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+#include "semeventwait-r0drv-solaris.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Waiter entry. Lives on the stack.
+ *
+ * @remarks Unfortunately, we cannot easily use cv_signal because we cannot
+ * distinguish between it and the spurious wakeups we get after fork.
+ * So, we keep an unprioritized FIFO with the sleeping threads.
+ */
+typedef struct RTSEMEVENTSOLENTRY
+{
+ /** The list node. */
+ RTLISTNODE Node;
+ /** The thread. */
+ kthread_t *pThread;
+ /** Set to @c true when waking up the thread by signal or destroy. */
+ uint32_t volatile fWokenUp;
+} RTSEMEVENTSOLENTRY;
+/** Pointer to waiter entry. */
+typedef RTSEMEVENTSOLENTRY *PRTSEMEVENTSOLENTRY;
+
+
+/**
+ * Solaris event semaphore.
+ */
+typedef struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The number of threads referencing this object. */
+ uint32_t volatile cRefs;
+ /** Set if the object is signalled when there are no waiters. */
+ bool fSignaled;
+ /** List of waiting and woken up threads. */
+ RTLISTANCHOR WaitList;
+ /** The Solaris mutex protecting this structure and pairing up the with the cv. */
+ kmutex_t Mtx;
+ /** The Solaris condition variable. */
+ kcondvar_t Cnd;
+} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL;
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *));
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+ AssertPtrReturn(phEventSem, VERR_INVALID_POINTER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->cRefs = 1;
+ pThis->fSignaled = false;
+ RTListInit(&pThis->WaitList);
+ mutex_init(&pThis->Mtx, "IPRT Event Semaphore", MUTEX_DRIVER, (void *)ipltospl(DISP_LEVEL));
+ cv_init(&pThis->Cnd, "IPRT CV", CV_DRIVER, NULL);
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventSolRetain(PRTSEMEVENTINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+ NOREF(cRefs);
+}
+
+
+/**
+ * The destruct.
+ *
+ * @param pThis The semaphore.
+ */
+static void rtR0SemEventSolDtor(PRTSEMEVENTINTERNAL pThis)
+{
+ Assert(pThis->u32Magic != RTSEMEVENT_MAGIC);
+ cv_destroy(&pThis->Cnd);
+ mutex_destroy(&pThis->Mtx);
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventSolRelease(PRTSEMEVENTINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ rtR0SemEventSolDtor(pThis);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ PRTSEMEVENTINTERNAL pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs > 0);
+ RT_ASSERT_INTS_ON();
+
+ mutex_enter(&pThis->Mtx);
+
+ /*
+ * Invalidate the semaphore.
+ */
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC);
+ ASMAtomicWriteBool(&pThis->fSignaled, false);
+
+ /*
+ * Abort and wake up all threads.
+ */
+ PRTSEMEVENTSOLENTRY pWaiter;
+ RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTSOLENTRY, Node)
+ {
+ pWaiter->fWokenUp = true;
+ }
+ cv_broadcast(&pThis->Cnd);
+
+ /*
+ * Release the reference from RTSemEventCreateEx.
+ */
+ mutex_exit(&pThis->Mtx);
+ rtR0SemEventSolRelease(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem;
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+
+ rtR0SemEventSolRetain(pThis);
+ rtR0SemSolWaitEnterMutexWithUnpinningHack(&pThis->Mtx);
+
+ /*
+ * Wake up one thread.
+ */
+ ASMAtomicWriteBool(&pThis->fSignaled, true);
+
+ PRTSEMEVENTSOLENTRY pWaiter;
+ RTListForEach(&pThis->WaitList, pWaiter, RTSEMEVENTSOLENTRY, Node)
+ {
+ if (!pWaiter->fWokenUp)
+ {
+ pWaiter->fWokenUp = true;
+ setrun(pWaiter->pThread);
+ ASMAtomicWriteBool(&pThis->fSignaled, false);
+ break;
+ }
+ }
+
+ mutex_exit(&pThis->Mtx);
+ rtR0SemEventSolRelease(pThis);
+
+#ifdef DEBUG_ramshankar
+ /** See @bugref{6318} comment#11 */
+ return VINF_SUCCESS;
+#endif
+ RT_ASSERT_PREEMPT_CPUID();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventWaitEx.
+ * @param uTimeout See RTSemEventWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventSolWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ rtR0SemEventSolRetain(pThis);
+ mutex_enter(&pThis->Mtx);
+
+ /*
+ * In the signaled state?
+ */
+ int rc;
+ if (ASMAtomicCmpXchgBool(&pThis->fSignaled, false, true))
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ RTR0SEMSOLWAIT Wait;
+ rc = rtR0SemSolWaitInit(&Wait, fFlags, uTimeout);
+ if (RT_SUCCESS(rc))
+ {
+ RTSEMEVENTSOLENTRY Waiter; /* ASSUMES we won't get swapped out while waiting (TS_DONT_SWAP). */
+ Waiter.pThread = curthread;
+ Waiter.fWokenUp = false;
+ RTListAppend(&pThis->WaitList, &Waiter.Node);
+
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (Waiter.fWokenUp)
+ rc = VINF_SUCCESS;
+ else if (rtR0SemSolWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemSolWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemSolWaitDoIt(&Wait, &pThis->Cnd, &pThis->Mtx, &Waiter.fWokenUp, false);
+ continue;
+ }
+ }
+ break;
+ }
+
+ rtR0SemSolWaitDelete(&Wait);
+ RTListNodeRemove(&Waiter.Node);
+ }
+ }
+
+ mutex_exit(&pThis->Mtx);
+ rtR0SemEventSolRelease(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventSolWait(hEventSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventSolWait(hEventSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventSolWait(hEventSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventGetResolution(void)
+{
+ return rtR0SemSolWaitGetResolution();
+}
+
+
+RTR0DECL(bool) RTSemEventIsSignalSafe(void)
+{
+ /* I don't trust Solaris not to preempt us. */
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/semeventmulti-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/semeventmulti-r0drv-solaris.c
new file mode 100644
index 00000000..04692b61
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/semeventmulti-r0drv-solaris.c
@@ -0,0 +1,372 @@
+/* $Id: semeventmulti-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 RTSEMEVENTMULTI_WITHOUT_REMAPPING
+#include "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+#include "semeventwait-r0drv-solaris.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name fStateAndGen values
+ * @{ */
+/** The state bit number. */
+#define RTSEMEVENTMULTISOL_STATE_BIT 0
+/** The state mask. */
+#define RTSEMEVENTMULTISOL_STATE_MASK RT_BIT_32(RTSEMEVENTMULTISOL_STATE_BIT)
+/** The generation mask. */
+#define RTSEMEVENTMULTISOL_GEN_MASK ~RTSEMEVENTMULTISOL_STATE_MASK
+/** The generation shift. */
+#define RTSEMEVENTMULTISOL_GEN_SHIFT 1
+/** The initial variable value. */
+#define RTSEMEVENTMULTISOL_STATE_GEN_INIT UINT32_C(0xfffffffc)
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Solaris multiple release event semaphore.
+ */
+typedef struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The number of references. */
+ uint32_t volatile cRefs;
+ /** The object state bit and generation counter.
+ * The generation counter is incremented every time the object is
+ * signalled. */
+ uint32_t volatile fStateAndGen;
+ /** The Solaris mutex protecting this structure and pairing up the with the cv. */
+ kmutex_t Mtx;
+ /** The Solaris condition variable. */
+ kcondvar_t Cnd;
+} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL;
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phEventMultiSem, VERR_INVALID_POINTER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ AssertCompile(sizeof(RTSEMEVENTMULTIINTERNAL) > sizeof(void *));
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+ pThis->cRefs = 1;
+ pThis->fStateAndGen = RTSEMEVENTMULTISOL_STATE_GEN_INIT;
+ mutex_init(&pThis->Mtx, "IPRT Multiple Release Event Semaphore", MUTEX_DRIVER, (void *)ipltospl(DISP_LEVEL));
+ cv_init(&pThis->Cnd, "IPRT CV", CV_DRIVER, NULL);
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Retain a reference to the semaphore.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiSolRetain(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs && cRefs < 100000);
+ NOREF(cRefs);
+}
+
+
+/**
+ * Destructor that is called when cRefs == 0.
+ *
+ * @param pThis The instance to destroy.
+ */
+static void rtSemEventMultiDtor(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ Assert(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC);
+ cv_destroy(&pThis->Cnd);
+ mutex_destroy(&pThis->Mtx);
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Release a reference, destroy the thing if necessary.
+ *
+ * @param pThis The semaphore.
+ */
+DECLINLINE(void) rtR0SemEventMultiSolRelease(PRTSEMEVENTMULTIINTERNAL pThis)
+{
+ if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0))
+ rtSemEventMultiDtor(pThis);
+}
+
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENTMULTI)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->cRefs > 0, ("pThis=%p cRefs=%d\n", pThis, pThis->cRefs), VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+
+ mutex_enter(&pThis->Mtx);
+
+ /* Invalidate the handle and wake up all threads that might be waiting on the semaphore. */
+ Assert(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+ ASMAtomicWriteU32(&pThis->u32Magic, RTSEMEVENTMULTI_MAGIC_DEAD);
+ ASMAtomicAndU32(&pThis->fStateAndGen, RTSEMEVENTMULTISOL_GEN_MASK);
+ cv_broadcast(&pThis->Cnd);
+
+ /* Drop the reference from RTSemEventMultiCreateEx. */
+ mutex_exit(&pThis->Mtx);
+ rtR0SemEventMultiSolRelease(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+ rtR0SemEventMultiSolRetain(pThis);
+ rtR0SemSolWaitEnterMutexWithUnpinningHack(&pThis->Mtx);
+ Assert(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ /*
+ * Do the job.
+ */
+ uint32_t fNew = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ fNew += 1 << RTSEMEVENTMULTISOL_GEN_SHIFT;
+ fNew |= RTSEMEVENTMULTISOL_STATE_MASK;
+ ASMAtomicWriteU32(&pThis->fStateAndGen, fNew);
+
+ cv_broadcast(&pThis->Cnd);
+
+ mutex_exit(&pThis->Mtx);
+
+ rtR0SemEventMultiSolRelease(pThis);
+#ifdef DEBUG_ramshankar
+ /** See @bugref{6318#c11}. */
+ return VINF_SUCCESS;
+#endif
+ RT_ASSERT_PREEMPT_CPUID();
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem;
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC,
+ ("pThis=%p u32Magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+
+ rtR0SemEventMultiSolRetain(pThis);
+ rtR0SemSolWaitEnterMutexWithUnpinningHack(&pThis->Mtx);
+ Assert(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ /*
+ * Do the job (could be done without the lock, but play safe).
+ */
+ ASMAtomicAndU32(&pThis->fStateAndGen, ~RTSEMEVENTMULTISOL_STATE_MASK);
+
+ mutex_exit(&pThis->Mtx);
+ rtR0SemEventMultiSolRelease(pThis);
+
+#ifdef DEBUG_ramshankar
+ /** See @bugref{6318#c11}. */
+ return VINF_SUCCESS;
+#endif
+ RT_ASSERT_PREEMPT_CPUID();
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug.
+ *
+ * @returns VBox status code.
+ * @param pThis The event semaphore.
+ * @param fFlags See RTSemEventMultiWaitEx.
+ * @param uTimeout See RTSemEventMultiWaitEx.
+ * @param pSrcPos The source code position of the wait.
+ */
+static int rtR0SemEventMultiSolWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ uint32_t fOrgStateAndGen;
+ int rc;
+
+ /*
+ * Validate the input.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+ rtR0SemEventMultiSolRetain(pThis);
+ mutex_enter(&pThis->Mtx); /* this could be moved down to the else, but play safe for now. */
+
+ /*
+ * Is the event already signalled or do we have to wait?
+ */
+ fOrgStateAndGen = ASMAtomicUoReadU32(&pThis->fStateAndGen);
+ if (fOrgStateAndGen & RTSEMEVENTMULTISOL_STATE_MASK)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * We have to wait.
+ */
+ RTR0SEMSOLWAIT Wait;
+ rc = rtR0SemSolWaitInit(&Wait, fFlags, uTimeout);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /* The destruction test. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else
+ {
+ /* Check the exit conditions. */
+ if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC))
+ rc = VERR_SEM_DESTROYED;
+ else if (ASMAtomicUoReadU32(&pThis->fStateAndGen) != fOrgStateAndGen)
+ rc = VINF_SUCCESS;
+ else if (rtR0SemSolWaitHasTimedOut(&Wait))
+ rc = VERR_TIMEOUT;
+ else if (rtR0SemSolWaitWasInterrupted(&Wait))
+ rc = VERR_INTERRUPTED;
+ else
+ {
+ /* Do the wait and then recheck the conditions. */
+ rtR0SemSolWaitDoIt(&Wait, &pThis->Cnd, &pThis->Mtx, &pThis->fStateAndGen, fOrgStateAndGen);
+ continue;
+ }
+ }
+ break;
+ }
+ rtR0SemSolWaitDelete(&Wait);
+ }
+ }
+
+ mutex_exit(&pThis->Mtx);
+ rtR0SemEventMultiSolRelease(pThis);
+ return rc;
+}
+
+
+
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtR0SemEventMultiSolWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtR0SemEventMultiSolWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtR0SemEventMultiSolWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+RTDECL(uint32_t) RTSemEventMultiGetResolution(void)
+{
+ return rtR0SemSolWaitGetResolution();
+}
+
+
+RTR0DECL(bool) RTSemEventMultiIsSignalSafe(void)
+{
+ /* Don't trust solaris not to preempt us. */
+ return false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/semeventwait-r0drv-solaris.h b/src/VBox/Runtime/r0drv/solaris/semeventwait-r0drv-solaris.h
new file mode 100644
index 00000000..2494d6f5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/semeventwait-r0drv-solaris.h
@@ -0,0 +1,508 @@
+/* $Id: semeventwait-r0drv-solaris.h $ */
+/** @file
+ * IPRT - Solaris Ring-0 Driver Helpers for Event Semaphore Waits.
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_solaris_semeventwait_r0drv_solaris_h
+#define IPRT_INCLUDED_SRC_r0drv_solaris_semeventwait_r0drv_solaris_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "the-solaris-kernel.h"
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+
+/** The resolution (nanoseconds) specified when using timeout_generic. */
+#define RTR0SEMSOLWAIT_RESOLUTION 50000
+
+/** Disables the cyclic fallback code for old S10 installs - see @bugref{5342}.
+ * @todo Fixed by @bugref{5595}, can be reenabled after checking out
+ * CY_HIGH_LEVEL. */
+#define RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+
+#define SOL_THREAD_TINTR_PTR ((kthread_t **)((char *)curthread + g_offrtSolThreadIntrThread))
+
+
+/**
+ * Solaris semaphore wait structure.
+ */
+typedef struct RTR0SEMSOLWAIT
+{
+ /** The absolute timeout given as nanoseconds since the start of the
+ * monotonic clock. */
+ uint64_t uNsAbsTimeout;
+ /** The timeout in nanoseconds relative to the start of the wait. */
+ uint64_t cNsRelTimeout;
+ /** The native timeout value. */
+ union
+ {
+ /** The timeout (in ticks) when fHighRes is false. */
+ clock_t lTimeout;
+ } u;
+ /** Set if we use high resolution timeouts. */
+ bool fHighRes;
+ /** Set if it's an indefinite wait. */
+ bool fIndefinite;
+ /** Set if the waiting thread is ready to be woken up.
+ * Avoids false setrun() calls due to temporary mutex exits. */
+ bool volatile fWantWakeup;
+ /** Set if we've already timed out.
+ * Set by rtR0SemSolWaitDoIt or rtR0SemSolWaitHighResTimeout, read by
+ * rtR0SemSolWaitHasTimedOut. */
+ bool volatile fTimedOut;
+ /** Whether the wait was interrupted. */
+ bool fInterrupted;
+ /** Interruptible or uninterruptible wait. */
+ bool fInterruptible;
+ /** The thread to wake up. */
+ kthread_t *pThread;
+#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+ /** Cylic timer ID (used by the timeout callback). */
+ cyclic_id_t idCy;
+#endif
+ /** The mutex associated with the condition variable wait. */
+ void volatile *pvMtx;
+} RTR0SEMSOLWAIT;
+/** Pointer to a solaris semaphore wait structure. */
+typedef RTR0SEMSOLWAIT *PRTR0SEMSOLWAIT;
+
+
+/**
+ * Initializes a wait.
+ *
+ * The caller MUST check the wait condition BEFORE calling this function or the
+ * timeout logic will be flawed.
+ *
+ * @returns VINF_SUCCESS or VERR_TIMEOUT.
+ * @param pWait The wait structure.
+ * @param fFlags The wait flags.
+ * @param uTimeout The timeout.
+ */
+DECLINLINE(int) rtR0SemSolWaitInit(PRTR0SEMSOLWAIT pWait, uint32_t fFlags, uint64_t uTimeout)
+{
+ /*
+ * Process the flags and timeout.
+ */
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_MILLISECS)
+ uTimeout = uTimeout < UINT64_MAX / RT_NS_1MS
+ ? uTimeout * RT_NS_1MS
+ : UINT64_MAX;
+ if (uTimeout == UINT64_MAX)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ {
+ uint64_t u64Now;
+ if (fFlags & RTSEMWAIT_FLAGS_RELATIVE)
+ {
+ if (uTimeout == 0)
+ return VERR_TIMEOUT;
+
+ u64Now = RTTimeSystemNanoTS();
+ pWait->cNsRelTimeout = uTimeout;
+ pWait->uNsAbsTimeout = u64Now + uTimeout;
+ if (pWait->uNsAbsTimeout < u64Now) /* overflow */
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ }
+ else
+ {
+ u64Now = RTTimeSystemNanoTS();
+ if (u64Now >= uTimeout)
+ return VERR_TIMEOUT;
+
+ pWait->cNsRelTimeout = uTimeout - u64Now;
+ pWait->uNsAbsTimeout = uTimeout;
+ }
+ }
+ }
+
+ if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE))
+ {
+ pWait->fIndefinite = false;
+ if ( ( (fFlags & (RTSEMWAIT_FLAGS_NANOSECS | RTSEMWAIT_FLAGS_ABSOLUTE))
+ || pWait->cNsRelTimeout < UINT32_C(1000000000) / 100 /*Hz*/ * 4)
+#ifdef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+ && g_pfnrtR0Sol_timeout_generic != NULL
+#endif
+ )
+ pWait->fHighRes = true;
+ else
+ {
+ uint64_t cTicks = NSEC_TO_TICK_ROUNDUP(uTimeout);
+ if (cTicks >= LONG_MAX)
+ fFlags |= RTSEMWAIT_FLAGS_INDEFINITE;
+ else
+ {
+ pWait->u.lTimeout = cTicks;
+ pWait->fHighRes = false;
+ }
+ }
+ }
+
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ {
+ pWait->fIndefinite = true;
+ pWait->fHighRes = false;
+ pWait->uNsAbsTimeout = UINT64_MAX;
+ pWait->cNsRelTimeout = UINT64_MAX;
+ pWait->u.lTimeout = LONG_MAX;
+ }
+
+ pWait->fWantWakeup = false;
+ pWait->fTimedOut = false;
+ pWait->fInterrupted = false;
+ pWait->fInterruptible = !!(fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE);
+ pWait->pThread = curthread;
+ pWait->pvMtx = NULL;
+#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+ pWait->idCy = CYCLIC_NONE;
+#endif
+
+ return VINF_SUCCESS;
+}
+
+
+#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+/**
+ * Cyclic timeout callback that sets the timeout indicator and wakes up the
+ * waiting thread.
+ *
+ * @param pvUser The wait structure.
+ */
+static void rtR0SemSolWaitHighResTimeout(void *pvUser)
+{
+ PRTR0SEMSOLWAIT pWait = (PRTR0SEMSOLWAIT)pvUser;
+ kthread_t *pThread = pWait->pThread;
+ kmutex_t *pMtx = (kmutex_t *)ASMAtomicReadPtr(&pWait->pvMtx);
+ if (RT_VALID_PTR(pMtx))
+ {
+ /* Enter the mutex here to make sure the thread has gone to sleep
+ before we wake it up.
+ Note: Trying to take the cpu_lock here doesn't work. */
+ mutex_enter(pMtx);
+ if (mutex_owner(&cpu_lock) == curthread)
+ {
+ cyclic_remove(pWait->idCy);
+ pWait->idCy = CYCLIC_NONE;
+ }
+ bool const fWantWakeup = pWait->fWantWakeup;
+ ASMAtomicWriteBool(&pWait->fTimedOut, true);
+ mutex_exit(pMtx);
+
+ if (fWantWakeup)
+ setrun(pThread);
+ }
+}
+#endif
+
+
+/**
+ * Timeout callback that sets the timeout indicator and wakes up the waiting
+ * thread.
+ *
+ * @param pvUser The wait structure.
+ */
+static void rtR0SemSolWaitTimeout(void *pvUser)
+{
+ PRTR0SEMSOLWAIT pWait = (PRTR0SEMSOLWAIT)pvUser;
+ kthread_t *pThread = pWait->pThread;
+ kmutex_t *pMtx = (kmutex_t *)ASMAtomicReadPtr((void * volatile *)&pWait->pvMtx);
+ if (RT_VALID_PTR(pMtx))
+ {
+ /* Enter the mutex here to make sure the thread has gone to sleep
+ before we wake it up. */
+ mutex_enter(pMtx);
+ bool const fWantWakeup = pWait->fWantWakeup;
+ ASMAtomicWriteBool(&pWait->fTimedOut, true);
+ mutex_exit(pMtx);
+
+ if (fWantWakeup)
+ setrun(pThread);
+ }
+}
+
+
+/**
+ * Do the actual wait.
+ *
+ * @param pWait The wait structure.
+ * @param pCnd The condition variable to wait on.
+ * @param pMtx The mutex related to the condition variable.
+ * The caller has entered this.
+ * @param pfState The state variable to check if have changed
+ * after leaving the mutex (spinlock).
+ * @param fCurState The current value of @a pfState. We'll return
+ * without sleeping if @a pfState doesn't hold
+ * this value after reacquiring the mutex.
+ *
+ * @remarks This must be call with the object mutex (spinlock) held.
+ */
+DECLINLINE(void) rtR0SemSolWaitDoIt(PRTR0SEMSOLWAIT pWait, kcondvar_t *pCnd, kmutex_t *pMtx,
+ uint32_t volatile *pfState, uint32_t const fCurState)
+{
+ union
+ {
+ callout_id_t idCo;
+ timeout_id_t idTom;
+ } u;
+
+ u.idCo = 0; /* Silence a spurious gcc [-Wmaybe-uninitialized] warning. */
+
+ /*
+ * Arm the timeout callback.
+ *
+ * We will have to leave the mutex (spinlock) when doing this because S10
+ * (didn't check S11) will not correctly preserve PIL across calls to
+ * timeout_generic() - @bugref{5595}. We do it for all timeout methods to
+ * be on the safe side, the nice sideeffect of which is that it solves the
+ * lock inversion problem found in @bugref{5342}.
+ */
+ bool const fHasTimeout = !pWait->fIndefinite;
+ bool fGoToSleep = !fHasTimeout;
+ if (fHasTimeout)
+ {
+ pWait->fWantWakeup = false; /* only want fTimedOut */
+ ASMAtomicWritePtr(&pWait->pvMtx, pMtx); /* atomic is paranoia */
+ mutex_exit(pMtx);
+
+ if (pWait->fHighRes)
+ {
+#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+ if (g_pfnrtR0Sol_timeout_generic != NULL)
+#endif
+ {
+ /*
+ * High resolution timeout - arm a high resolution timeout callback
+ * for waking up the thread at the desired time.
+ */
+ u.idCo = g_pfnrtR0Sol_timeout_generic(CALLOUT_REALTIME, rtR0SemSolWaitTimeout, pWait,
+ pWait->uNsAbsTimeout, RTR0SEMSOLWAIT_RESOLUTION,
+ CALLOUT_FLAG_ABSOLUTE);
+ }
+#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+ else
+ {
+ /*
+ * High resolution timeout - arm a one-shot cyclic for waking up
+ * the thread at the desired time.
+ */
+ cyc_handler_t Cyh;
+ Cyh.cyh_arg = pWait;
+ Cyh.cyh_func = rtR0SemSolWaitHighResTimeout;
+ Cyh.cyh_level = CY_LOW_LEVEL; /// @todo try CY_LOCK_LEVEL and CY_HIGH_LEVEL?
+
+ cyc_time_t Cyt;
+ Cyt.cyt_when = pWait->uNsAbsTimeout;
+ Cyt.cyt_interval = UINT64_C(1000000000) * 60;
+
+ mutex_enter(&cpu_lock);
+ pWait->idCy = cyclic_add(&Cyh, &Cyt);
+ mutex_exit(&cpu_lock);
+ }
+#endif
+ }
+ else
+ {
+ /*
+ * Normal timeout.
+ * We're better off with our own callback like on the timeout man page,
+ * than calling cv_timedwait[_sig]().
+ */
+ u.idTom = realtime_timeout(rtR0SemSolWaitTimeout, pWait, pWait->u.lTimeout);
+ }
+
+ /*
+ * Reacquire the mutex and check if the sleep condition still holds and
+ * that we didn't already time out.
+ */
+ mutex_enter(pMtx);
+ pWait->fWantWakeup = true;
+ fGoToSleep = !ASMAtomicUoReadBool(&pWait->fTimedOut)
+ && ASMAtomicReadU32(pfState) == fCurState;
+ }
+
+ /*
+ * Do the waiting if that's still desirable.
+ * (rc > 0 - normal wake-up; rc == 0 - interruption; rc == -1 - timeout)
+ */
+ if (fGoToSleep)
+ {
+ if (pWait->fInterruptible)
+ {
+ int rc = cv_wait_sig(pCnd, pMtx);
+ if (RT_UNLIKELY(rc <= 0))
+ {
+ if (RT_LIKELY(rc == 0))
+ pWait->fInterrupted = true;
+ else
+ AssertMsgFailed(("rc=%d\n", rc)); /* no timeouts, see above! */
+ }
+ }
+ else
+ cv_wait(pCnd, pMtx);
+ }
+
+ /*
+ * Remove the timeout callback. Drop the lock while we're doing that
+ * to reduce lock contention / deadlocks. Before dropping the lock,
+ * indicate that the callback shouldn't do anything.
+ *
+ * (Too bad we are stuck with the cv_* API here, it's doing a little
+ * bit too much.)
+ */
+ if (fHasTimeout)
+ {
+ pWait->fWantWakeup = false;
+ ASMAtomicWritePtr(&pWait->pvMtx, NULL);
+ mutex_exit(pMtx);
+
+ if (pWait->fHighRes)
+ {
+#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+ if (g_pfnrtR0Sol_timeout_generic != NULL)
+#endif
+ g_pfnrtR0Sol_untimeout_generic(u.idCo, 0 /*nowait*/);
+#ifndef RTR0SEMSOLWAIT_NO_OLD_S10_FALLBACK
+ else
+ {
+ mutex_enter(&cpu_lock);
+ if (pWait->idCy != CYCLIC_NONE)
+ {
+ cyclic_remove(pWait->idCy);
+ pWait->idCy = CYCLIC_NONE;
+ }
+ mutex_exit(&cpu_lock);
+ }
+#endif
+ }
+ else
+ untimeout(u.idTom);
+
+ mutex_enter(pMtx);
+ }
+}
+
+
+/**
+ * Checks if a solaris wait was interrupted.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ * @remarks This shall be called before the first rtR0SemSolWaitDoIt().
+ */
+DECLINLINE(bool) rtR0SemSolWaitWasInterrupted(PRTR0SEMSOLWAIT pWait)
+{
+ return pWait->fInterrupted;
+}
+
+
+/**
+ * Checks if a solaris wait has timed out.
+ *
+ * @returns true / false
+ * @param pWait The wait structure.
+ */
+DECLINLINE(bool) rtR0SemSolWaitHasTimedOut(PRTR0SEMSOLWAIT pWait)
+{
+ return pWait->fTimedOut;
+}
+
+
+/**
+ * Deletes a solaris wait.
+ *
+ * @param pWait The wait structure.
+ */
+DECLINLINE(void) rtR0SemSolWaitDelete(PRTR0SEMSOLWAIT pWait)
+{
+ pWait->pThread = NULL;
+}
+
+
+/**
+ * Enters the mutex, unpinning the underlying current thread if contended and
+ * we're on an interrupt thread.
+ *
+ * The unpinning is done to prevent a deadlock, see s this could lead to a
+ * deadlock (see @bugref{4259} for the full explanation)
+ *
+ * @param pMtx The mutex to enter.
+ */
+DECLINLINE(void) rtR0SemSolWaitEnterMutexWithUnpinningHack(kmutex_t *pMtx)
+{
+ int fAcquired = mutex_tryenter(pMtx);
+ if (!fAcquired)
+ {
+ /*
+ * Note! This assumes nobody is using the RTThreadPreemptDisable() in an
+ * interrupt context and expects it to work right. The swtch will
+ * result in a voluntary preemption. To fix this, we would have to
+ * do our own counting in RTThreadPreemptDisable/Restore() like we do
+ * on systems which doesn't do preemption (OS/2, linux, ...) and
+ * check whether preemption was disabled via RTThreadPreemptDisable()
+ * or not and only call swtch if RTThreadPreemptDisable() wasn't called.
+ */
+ kthread_t **ppIntrThread = SOL_THREAD_TINTR_PTR;
+ if ( *ppIntrThread
+ && getpil() < DISP_LEVEL)
+ {
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ RTThreadPreemptDisable(&PreemptState);
+ preempt();
+ RTThreadPreemptRestore(&PreemptState);
+ }
+ mutex_enter(pMtx);
+ }
+}
+
+
+/**
+ * Gets the max resolution of the timeout machinery.
+ *
+ * @returns Resolution specified in nanoseconds.
+ */
+DECLINLINE(uint32_t) rtR0SemSolWaitGetResolution(void)
+{
+ return g_pfnrtR0Sol_timeout_generic != NULL
+ ? RTR0SEMSOLWAIT_RESOLUTION
+ : cyclic_getres();
+}
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_solaris_semeventwait_r0drv_solaris_h */
+
diff --git a/src/VBox/Runtime/r0drv/solaris/semfastmutex-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/semfastmutex-r0drv-solaris.c
new file mode 100644
index 00000000..22a15aef
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/semfastmutex-r0drv-solaris.c
@@ -0,0 +1,130 @@
+/* $Id: semfastmutex-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Fast Mutex Semaphores, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the Solaris mutex.
+ */
+typedef struct RTSEMFASTMUTEXINTERNAL
+{
+ /** Magic value (RTSEMFASTMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The Solaris mutex. */
+ krwlock_t Mtx;
+} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL;
+
+
+
+RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx)
+{
+ AssertCompile(sizeof(RTSEMFASTMUTEXINTERNAL) > sizeof(void *));
+ AssertPtrReturn(phFastMtx, VERR_INVALID_POINTER);
+ RT_ASSERT_PREEMPTIBLE();
+
+ PRTSEMFASTMUTEXINTERNAL pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMFASTMUTEX_MAGIC;
+ rw_init (&pThis->Mtx, "RWLOCK", RW_DRIVER, NULL);
+
+ *phFastMtx = pThis;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ if (pThis == NIL_RTSEMFASTMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+
+ ASMAtomicXchgU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD);
+ rw_destroy(&pThis->Mtx);
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_PREEMPTIBLE();
+
+ rw_enter(&pThis->Mtx, RW_WRITER);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx)
+{
+ PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE);
+ RT_ASSERT_INTS_ON();
+
+ rw_exit(&pThis->Mtx);
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/semmutex-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/semmutex-r0drv-solaris.c
new file mode 100644
index 00000000..e8081482
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/semmutex-r0drv-solaris.c
@@ -0,0 +1,397 @@
+/* $Id: semmutex-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Mutex Semaphores, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 RTSEMMUTEX_WITHOUT_REMAPPING
+#include "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/semaphore.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/mem.h>
+#include <iprt/err.h>
+#include <iprt/list.h>
+#include <iprt/thread.h>
+
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the solaris semaphore structure.
+ */
+typedef struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** The number of recursions. */
+ uint32_t cRecursions;
+ /** The number of threads waiting for the mutex. */
+ uint32_t volatile cWaiters;
+ /** The number of threads referencing us. */
+ uint32_t volatile cRefs;
+ /** The owner thread, NIL_RTNATIVETHREAD if none. */
+ RTNATIVETHREAD hOwnerThread;
+ /** The mutex object for synchronization. */
+ kmutex_t Mtx;
+ /** The condition variable for synchronization. */
+ kcondvar_t Cnd;
+} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL;
+
+
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMtx)
+{
+ /*
+ * Allocate.
+ */
+ PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (RT_UNLIKELY(!pThis))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize.
+ */
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ pThis->cRecursions = 0;
+ pThis->cWaiters = 0;
+ pThis->cRefs = 1;
+ pThis->hOwnerThread = NIL_RTNATIVETHREAD;
+ mutex_init(&pThis->Mtx, "IPRT Mutex", MUTEX_DRIVER, (void *)ipltospl(DISP_LEVEL));
+ cv_init(&pThis->Cnd, "IPRT CVM", CV_DRIVER, NULL);
+ *phMtx = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMtx)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMtx;
+
+ /*
+ * Validate.
+ */
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+ mutex_enter(&pThis->Mtx);
+
+ ASMAtomicDecU32(&pThis->cRefs);
+
+ /*
+ * Invalidate the magic to indicate the mutex is being destroyed.
+ */
+ ASMAtomicIncU32(&pThis->u32Magic);
+ if (pThis->cWaiters > 0)
+ {
+ /*
+ * Wake up all waiters, last waiter thread cleans up.
+ */
+ cv_broadcast(&pThis->Cnd);
+ mutex_exit(&pThis->Mtx);
+ }
+ else if (pThis->cRefs == 0)
+ {
+ /*
+ * We're the last waiter, destroy.
+ */
+ mutex_exit(&pThis->Mtx);
+ cv_destroy(&pThis->Cnd);
+ mutex_destroy(&pThis->Mtx);
+ RTMemFree(pThis);
+ }
+ else
+ {
+ /*
+ * We're not the last waiting thread to be woken up. Just relinquish & bail.
+ */
+ mutex_exit(&pThis->Mtx);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtSemMutexSolRequest that handles the case where we go to sleep.
+ *
+ * @returns VINF_SUCCESS, VERR_INTERRUPTED, or VERR_SEM_DESTROYED.
+ * Returns without owning the mutex.
+ * @param pThis The mutex instance.
+ * @param cMillies The timeout, must be > 0 or RT_INDEFINITE_WAIT.
+ * @param fInterruptible The wait type.
+ *
+ * @remarks This needs to be called with the mutex object held!
+ */
+static int rtSemMutexSolRequestSleep(PRTSEMMUTEXINTERNAL pThis, RTMSINTERVAL cMillies,
+ bool fInterruptible)
+{
+ int rc = VERR_GENERAL_FAILURE;
+ Assert(cMillies > 0);
+
+ /*
+ * Now we wait (sleep; although might spin and then sleep) & reference the mutex.
+ */
+ ASMAtomicIncU32(&pThis->cWaiters);
+ ASMAtomicIncU32(&pThis->cRefs);
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ clock_t cTicks = drv_usectohz((clock_t)(cMillies * 1000L));
+ clock_t cTimeout = ddi_get_lbolt();
+ cTimeout += cTicks;
+ if (fInterruptible)
+ rc = cv_timedwait_sig(&pThis->Cnd, &pThis->Mtx, cTimeout);
+ else
+ rc = cv_timedwait(&pThis->Cnd, &pThis->Mtx, cTimeout);
+ }
+ else
+ {
+ if (fInterruptible)
+ rc = cv_wait_sig(&pThis->Cnd, &pThis->Mtx);
+ else
+ {
+ cv_wait(&pThis->Cnd, &pThis->Mtx);
+ rc = 1;
+ }
+ }
+
+ ASMAtomicDecU32(&pThis->cWaiters);
+ if (rc > 0)
+ {
+ if (pThis->u32Magic == RTSEMMUTEX_MAGIC)
+ {
+ if (pThis->hOwnerThread == NIL_RTNATIVETHREAD)
+ {
+ /*
+ * Woken up by a release from another thread.
+ */
+ Assert(pThis->cRecursions == 0);
+ pThis->cRecursions = 1;
+ pThis->hOwnerThread = RTThreadNativeSelf();
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /*
+ * Interrupted by some signal.
+ */
+ rc = VERR_INTERRUPTED;
+ }
+ }
+ else
+ {
+ /*
+ * Awakened due to the destruction-in-progress broadcast.
+ * We will cleanup if we're the last waiter.
+ */
+ rc = VERR_SEM_DESTROYED;
+ }
+ }
+ else if (rc == -1)
+ {
+ /*
+ * Timed out.
+ */
+ rc = VERR_TIMEOUT;
+ }
+ else
+ {
+ /*
+ * Condition may not have been met, returned due to pending signal.
+ */
+ rc = VERR_INTERRUPTED;
+ }
+
+ if (!ASMAtomicDecU32(&pThis->cRefs))
+ {
+ Assert(RT_FAILURE_NP(rc));
+ mutex_exit(&pThis->Mtx);
+ cv_destroy(&pThis->Cnd);
+ mutex_destroy(&pThis->Mtx);
+ RTMemFree(pThis);
+ return rc;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Internal worker.
+ */
+DECLINLINE(int) rtSemMutexSolRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, bool fInterruptible)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ int rc = VERR_GENERAL_FAILURE;
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+ Assert(pThis->cRefs >= 1);
+
+ /*
+ * Lock it and check if it's a recursion.
+ */
+ mutex_enter(&pThis->Mtx);
+ if (pThis->hOwnerThread == RTThreadNativeSelf())
+ {
+ pThis->cRecursions++;
+ Assert(pThis->cRecursions > 1);
+ Assert(pThis->cRecursions < 256);
+ rc = VINF_SUCCESS;
+ }
+ /*
+ * Not a recursion, claim the unowned mutex if we're there are no waiters.
+ */
+ else if ( pThis->hOwnerThread == NIL_RTNATIVETHREAD
+ && pThis->cWaiters == 0)
+ {
+ pThis->cRecursions = 1;
+ pThis->hOwnerThread = RTThreadNativeSelf();
+ rc = VINF_SUCCESS;
+ }
+ /*
+ * A polling call?
+ */
+ else if (cMillies == 0)
+ rc = VERR_TIMEOUT;
+ /*
+ * No, we really need to get to sleep.
+ */
+ else
+ rc = rtSemMutexSolRequestSleep(pThis, cMillies, fInterruptible);
+
+ mutex_exit(&pThis->Mtx);
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexSolRequest(hMutexSem, cMillies, false /*fInterruptible*/);
+}
+
+
+RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequest(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+ return rtSemMutexSolRequest(hMutexSem, cMillies, true /*fInterruptible*/);
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ return RTSemMutexRequestNoResume(hMutexSem, cMillies);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMtx)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMtx;
+ int rc;
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE);
+
+ /*
+ * Take the lock and release one recursion.
+ */
+ mutex_enter(&pThis->Mtx);
+ if (pThis->hOwnerThread == RTThreadNativeSelf())
+ {
+ Assert(pThis->cRecursions > 0);
+ if (--pThis->cRecursions == 0)
+ {
+ pThis->hOwnerThread = NIL_RTNATIVETHREAD;
+
+ /*
+ * If there are any waiters, signal one of them.
+ */
+ if (pThis->cWaiters > 0)
+ cv_signal(&pThis->Cnd);
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NOT_OWNER;
+
+ mutex_exit(&pThis->Mtx);
+ return rc;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ PRTSEMMUTEXINTERNAL pThis = hMutexSem;
+ bool fOwned = false;
+
+ /*
+ * Validate.
+ */
+ AssertPtrReturn(pThis, false);
+ AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), false);
+
+ /*
+ * Check if this is the owner.
+ */
+ mutex_enter(&pThis->Mtx);
+ fOwned = pThis->hOwnerThread != NIL_RTNATIVETHREAD;
+ mutex_exit(&pThis->Mtx);
+
+ return fOwned;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/spinlock-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/spinlock-r0drv-solaris.c
new file mode 100644
index 00000000..9032d810
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/spinlock-r0drv-solaris.c
@@ -0,0 +1,214 @@
+/* $Id: spinlock-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Spinlocks, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/spinlock.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Wrapper for the struct mutex type.
+ */
+typedef struct RTSPINLOCKINTERNAL
+{
+ /** Spinlock magic value (RTSPINLOCK_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Spinlock creation flags. */
+ uint32_t fFlags;
+ /** Saved interrupt flag. */
+ uint32_t volatile fIntSaved;
+ /** A Solaris spinlock. */
+ kmutex_t Mtx;
+#ifdef RT_MORE_STRICT
+ /** The idAssertCpu variable before acquring the lock for asserting after
+ * releasing the spinlock. */
+ RTCPUID volatile idAssertCpu;
+ /** The CPU that owns the lock. */
+ RTCPUID volatile idCpuOwner;
+#endif
+} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL;
+
+
+
+RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER);
+
+ /*
+ * Allocate.
+ */
+ AssertCompile(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *));
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize & return.
+ */
+ pThis->u32Magic = RTSPINLOCK_MAGIC;
+ pThis->fFlags = fFlags;
+ pThis->fIntSaved = 0;
+ /** @todo Consider different PIL when not interrupt safe requirement. */
+ mutex_init(&pThis->Mtx, "IPRT Spinlock", MUTEX_SPIN, (void *)ipltospl(PIL_MAX));
+ *pSpinlock = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock)
+{
+ /*
+ * Validate input.
+ */
+ RT_ASSERT_INTS_ON();
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ if (!pThis)
+ return VERR_INVALID_PARAMETER;
+ AssertMsgReturn(pThis->u32Magic == RTSPINLOCK_MAGIC,
+ ("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic),
+ VERR_INVALID_PARAMETER);
+
+ /*
+ * Make the lock invalid and release the memory.
+ */
+ ASMAtomicIncU32(&pThis->u32Magic);
+ mutex_destroy(&pThis->Mtx);
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ RT_ASSERT_PREEMPT_CPUID_VAR();
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint32_t fIntSaved = ASMIntDisableFlags();
+#endif
+ mutex_enter(&pThis->Mtx);
+
+ /*
+ * Solaris 10 doesn't preserve the interrupt flag, but since we're at PIL_MAX we should be
+ * fine and not get interrupts while lock is held. Re-disable interrupts to not upset
+ * assertions & assumptions callers might have.
+ */
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMIntDisable();
+#endif
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ Assert(!ASMIntAreEnabled());
+#endif
+ pThis->fIntSaved = fIntSaved;
+ }
+ else
+ {
+#if defined(RT_STRICT) && (defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86))
+ bool fIntsOn = ASMIntAreEnabled();
+#endif
+
+ mutex_enter(&pThis->Mtx);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ AssertMsg(fIntsOn == ASMIntAreEnabled(), ("fIntsOn=%RTbool\n", fIntsOn));
+#endif
+ }
+
+ RT_ASSERT_PREEMPT_CPUID_SPIN_ACQUIRED(pThis);
+}
+
+
+RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock)
+{
+ PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock;
+ RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE_VARS();
+
+ AssertPtr(pThis);
+ Assert(pThis->u32Magic == RTSPINLOCK_MAGIC);
+ RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE(pThis);
+
+ if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE)
+ {
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ uint32_t fIntSaved = pThis->fIntSaved;
+ pThis->fIntSaved = 0;
+#endif
+ mutex_exit(&pThis->Mtx);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ ASMSetFlags(fIntSaved);
+#endif
+ }
+ else
+ {
+#if defined(RT_STRICT) && (defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86))
+ bool fIntsOn = ASMIntAreEnabled();
+#endif
+
+ mutex_exit(&pThis->Mtx);
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ AssertMsg(fIntsOn == ASMIntAreEnabled(), ("fIntsOn=%RTbool\n", fIntsOn));
+#endif
+ }
+
+ RT_ASSERT_PREEMPT_CPUID();
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/the-solaris-kernel.h b/src/VBox/Runtime/r0drv/solaris/the-solaris-kernel.h
new file mode 100644
index 00000000..3cadd282
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/the-solaris-kernel.h
@@ -0,0 +1,244 @@
+/* $Id: the-solaris-kernel.h $ */
+/** @file
+ * IPRT - Include all necessary headers for the Solaris kernel.
+ */
+
+/*
+ * 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 IPRT_INCLUDED_SRC_r0drv_solaris_the_solaris_kernel_h
+#define IPRT_INCLUDED_SRC_r0drv_solaris_the_solaris_kernel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <sys/kmem.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/thread.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sdt.h>
+#include <sys/schedctl.h>
+#include <sys/time.h>
+#include <sys/sysmacros.h>
+#include <sys/cmn_err.h>
+#include <sys/vmsystm.h>
+#include <sys/cyclic.h>
+#include <sys/class.h>
+#include <sys/cpuvar.h>
+#include <sys/archsystm.h>
+#include <sys/x_call.h> /* in platform dir */
+#include <sys/x86_archext.h>
+#include <vm/hat.h>
+#include <vm/seg_vn.h>
+#include <vm/seg_kmem.h>
+#include <vm/page.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/spl.h>
+#include <sys/archsystm.h>
+#include <sys/callo.h>
+#include <sys/kobj.h>
+#include <sys/ctf_api.h>
+#include <sys/modctl.h>
+#include <sys/proc.h>
+#include <sys/t_lock.h>
+
+#undef u /* /usr/include/sys/user.h:249:1 is where this is defined to (curproc->p_user). very cool. */
+
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/dbg.h>
+
+RT_C_DECLS_BEGIN
+
+/* IPRT functions. */
+DECLHIDDEN(void *) rtR0SolMemAlloc(uint64_t cbPhysHi, uint64_t *puPhys, size_t cb, uint64_t cbAlign, bool fContig);
+DECLHIDDEN(void) rtR0SolMemFree(void *pv, size_t cb);
+
+
+/* Solaris functions. */
+typedef callout_id_t (*PFNSOL_timeout_generic)(int type, void (*func)(void *),
+ void *arg, hrtime_t expiration,
+ hrtime_t resultion, int flags);
+typedef hrtime_t (*PFNSOL_untimeout_generic)(callout_id_t id, int nowait);
+typedef int (*PFNSOL_cyclic_reprogram)(cyclic_id_t id, hrtime_t expiration);
+typedef void (*PFNSOL_contig_free)(void *addr, size_t size);
+typedef int (*PFNSOL_page_noreloc_supported)(size_t cbPageSize);
+
+/* IPRT globals. */
+extern bool g_frtSolSplSetsEIF;
+extern RTCPUSET g_rtMpSolCpuSet;
+extern PFNSOL_timeout_generic g_pfnrtR0Sol_timeout_generic;
+extern PFNSOL_untimeout_generic g_pfnrtR0Sol_untimeout_generic;
+extern PFNSOL_cyclic_reprogram g_pfnrtR0Sol_cyclic_reprogram;
+extern PFNSOL_contig_free g_pfnrtR0Sol_contig_free;
+extern PFNSOL_page_noreloc_supported g_pfnrtR0Sol_page_noreloc_supported;
+extern size_t g_offrtSolThreadPreempt;
+extern size_t g_offrtSolThreadIntrThread;
+extern size_t g_offrtSolThreadLock;
+extern size_t g_offrtSolThreadProc;
+extern size_t g_offrtSolThreadId;
+extern size_t g_offrtSolCpuPreempt;
+extern size_t g_offrtSolCpuForceKernelPreempt;
+extern bool g_frtSolInitDone;
+extern RTDBGKRNLINFO g_hKrnlDbgInfo;
+
+/*
+ * Workarounds for running on old versions of solaris with different cross call
+ * interfaces. If we find xc_init_cpu() in the kernel, then just use the
+ * defined interfaces for xc_call() from the include file where the xc_call()
+ * interfaces just takes a pointer to a ulong_t array. The array must be long
+ * enough to hold "ncpus" bits at runtime.
+
+ * The reason for the hacks is that using the type "cpuset_t" is pretty much
+ * impossible from code built outside the Solaris source repository that wants
+ * to run on multiple releases of Solaris.
+ *
+ * For old style xc_call()s, 32 bit solaris and older 64 bit versions use
+ * "ulong_t" as cpuset_t.
+ *
+ * Later versions of 64 bit Solaris used: struct {ulong_t words[x];}
+ * where "x" depends on NCPU.
+ *
+ * We detect the difference in 64 bit support by checking the kernel value of
+ * max_cpuid, which always holds the compiled value of NCPU - 1.
+ *
+ * If Solaris increases NCPU to more than 256, VBox will continue to work on
+ * all versions of Solaris as long as the number of installed CPUs in the
+ * machine is <= IPRT_SOLARIS_NCPUS. If IPRT_SOLARIS_NCPUS is increased, this
+ * code has to be re-written some to provide compatibility with older Solaris
+ * which expects cpuset_t to be based on NCPU==256 -- or we discontinue
+ * support of old Nevada/S10.
+ */
+#define IPRT_SOL_NCPUS 256
+#define IPRT_SOL_SET_WORDS (IPRT_SOL_NCPUS / (sizeof(ulong_t) * 8))
+#define IPRT_SOL_X_CALL_HIPRI (2) /* for Old Solaris interface */
+typedef struct RTSOLCPUSET
+{
+ ulong_t auCpus[IPRT_SOL_SET_WORDS];
+} RTSOLCPUSET;
+typedef RTSOLCPUSET *PRTSOLCPUSET;
+
+/* Avoid warnings even if it means more typing... */
+typedef struct RTR0FNSOLXCCALL
+{
+ union
+ {
+ void *(*pfnSol_xc_call) (xc_arg_t, xc_arg_t, xc_arg_t, ulong_t *, xc_func_t);
+ void *(*pfnSol_xc_call_old) (xc_arg_t, xc_arg_t, xc_arg_t, int, RTSOLCPUSET, xc_func_t);
+ void *(*pfnSol_xc_call_old_ulong)(xc_arg_t, xc_arg_t, xc_arg_t, int, ulong_t, xc_func_t);
+ } u;
+} RTR0FNSOLXCCALL;
+typedef RTR0FNSOLXCCALL *PRTR0FNSOLXCCALL;
+
+extern RTR0FNSOLXCCALL g_rtSolXcCall;
+extern bool g_frtSolOldIPI;
+extern bool g_frtSolOldIPIUlong;
+
+/*
+ * Thread-context hooks.
+ * Workarounds for older Solaris versions that did not have the exitctx() callback.
+ */
+typedef struct RTR0FNSOLTHREADCTX
+{
+ union
+ {
+ void *(*pfnSol_installctx) (kthread_t *pThread, void *pvArg,
+ void (*pfnSave)(void *pvArg),
+ void (*pfnRestore)(void *pvArg),
+ void (*pfnFork)(void *pvThread, void *pvThreadFork),
+ void (*pfnLwpCreate)(void *pvThread, void *pvThreadCreate),
+ void (*pfnExit)(void *pvThread),
+ void (*pfnFree)(void *pvArg, int fIsExec));
+
+ void *(*pfnSol_installctx_old) (kthread_t *pThread, void *pvArg,
+ void (*pfnSave)(void *pvArg),
+ void (*pfnRestore)(void *pvArg),
+ void (*pfnFork)(void *pvThread, void *pvThreadFork),
+ void (*pfnLwpCreate)(void *pvThread, void *pvThreadCreate),
+ void (*pfnFree)(void *pvArg, int fIsExec));
+ } Install;
+
+ union
+ {
+ int (*pfnSol_removectx) (kthread_t *pThread, void *pvArg,
+ void (*pfnSave)(void *pvArg),
+ void (*pfnRestore)(void *pvArg),
+ void (*pfnFork)(void *pvThread, void *pvThreadFork),
+ void (*pfnLwpCreate)(void *pvThread, void *pvThreadCreate),
+ void (*pfnExit)(void *pvThread),
+ void (*pfnFree)(void *pvArg, int fIsExec));
+
+ int (*pfnSol_removectx_old) (kthread_t *pThread, void *pvArg,
+ void (*pfnSave)(void *pvArg),
+ void (*pfnRestore)(void *pvArg),
+ void (*pfnFork)(void *pvThread, void *pvThreadFork),
+ void (*pfnLwpCreate)(void *pvThread, void *pvThreadCreate),
+ void (*pfnFree)(void *pvArg, int fIsExec));
+ } Remove;
+} RTR0FNSOLTHREADCTX;
+typedef RTR0FNSOLTHREADCTX *PRTR0FNSOLTHREADCTX;
+
+extern RTR0FNSOLTHREADCTX g_rtSolThreadCtx;
+extern bool g_frtSolOldThreadCtx;
+
+/*
+ * Workaround for older Solaris versions which called map_addr()/choose_addr()/
+ * map_addr_proc() with an 'alignment' argument that was removed in Solaris
+ * 11.4.
+ */
+typedef struct RTR0FNSOLMAPADDR
+{
+ union
+ {
+ void *(*pfnSol_map_addr) (caddr_t *, size_t, offset_t, uint_t);
+ void *(*pfnSol_map_addr_old) (caddr_t *, size_t, offset_t, int, uint_t);
+ } u;
+} RTR0FNSOLMAPADDR;
+typedef RTR0FNSOLMAPADDR *PRTR0FNSOLMAPADDR;
+
+extern RTR0FNSOLMAPADDR g_rtSolMapAddr;
+extern bool g_frtSolOldMapAddr;
+
+/* Solaris globals. */
+extern uintptr_t kernelbase;
+
+/* Misc stuff from newer kernels. */
+#ifndef CALLOUT_FLAG_ABSOLUTE
+# define CALLOUT_FLAG_ABSOLUTE 2
+#endif
+
+RT_C_DECLS_END
+
+#endif /* !IPRT_INCLUDED_SRC_r0drv_solaris_the_solaris_kernel_h */
+
diff --git a/src/VBox/Runtime/r0drv/solaris/thread-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/thread-r0drv-solaris.c
new file mode 100644
index 00000000..0fefabe9
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/thread-r0drv-solaris.c
@@ -0,0 +1,195 @@
+/* $Id: thread-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Threads, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mp.h>
+
+#define SOL_THREAD_PREEMPT (*((char *)curthread + g_offrtSolThreadPreempt))
+#define SOL_CPU_RUNRUN (*((char *)CPU + g_offrtSolCpuPreempt))
+#define SOL_CPU_KPRUNRUN (*((char *)CPU + g_offrtSolCpuForceKernelPreempt))
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)curthread;
+}
+
+
+static int rtR0ThreadSolSleepCommon(RTMSINTERVAL cMillies)
+{
+ clock_t cTicks;
+ RT_ASSERT_PREEMPTIBLE();
+
+ if (!cMillies)
+ {
+ RTThreadYield();
+ return VINF_SUCCESS;
+ }
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ cTicks = drv_usectohz((clock_t)(cMillies * 1000L));
+ else
+ cTicks = 0;
+
+ delay(cTicks);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadSolSleepCommon(cMillies);
+}
+
+
+RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ return rtR0ThreadSolSleepCommon(cMillies);
+}
+
+
+RTDECL(bool) RTThreadYield(void)
+{
+ RT_ASSERT_PREEMPTIBLE();
+
+ RTTHREADPREEMPTSTATE PreemptState = RTTHREADPREEMPTSTATE_INITIALIZER;
+ RTThreadPreemptDisable(&PreemptState);
+
+ char const bThreadPreempt = SOL_THREAD_PREEMPT;
+ char const bForcePreempt = SOL_CPU_KPRUNRUN;
+ bool fWillYield = false;
+ Assert(bThreadPreempt >= 1);
+
+ /*
+ * If we are the last preemption enabler for this thread and if force
+ * preemption is set on the CPU, only then we are guaranteed to be preempted.
+ */
+ if (bThreadPreempt == 1 && bForcePreempt != 0)
+ fWillYield = true;
+
+ RTThreadPreemptRestore(&PreemptState);
+ return fWillYield;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+ if (RT_UNLIKELY(g_frtSolInitDone == false))
+ {
+ cmn_err(CE_CONT, "!RTThreadPreemptIsEnabled called before RTR0Init!\n");
+ return true;
+ }
+
+ bool fThreadPreempt = false;
+ if (SOL_THREAD_PREEMPT == 0)
+ fThreadPreempt = true;
+
+ if (!fThreadPreempt)
+ return false;
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+ if (!ASMIntAreEnabled())
+ return false;
+#endif
+ if (getpil() >= DISP_LEVEL)
+ return false;
+ return true;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+
+ char const bPreempt = SOL_CPU_RUNRUN;
+ char const bForcePreempt = SOL_CPU_KPRUNRUN;
+ return (bPreempt != 0 || bForcePreempt != 0);
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPendingTrusty(void)
+{
+ /* yes, RTThreadPreemptIsPending is reliable. */
+ return true;
+}
+
+
+RTDECL(bool) RTThreadPreemptIsPossible(void)
+{
+ /* yes, kernel preemption is possible. */
+ return true;
+}
+
+
+RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+
+ SOL_THREAD_PREEMPT++;
+ Assert(SOL_THREAD_PREEMPT >= 1);
+
+ RT_ASSERT_PREEMPT_CPUID_DISABLE(pState);
+}
+
+
+RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState)
+{
+ AssertPtr(pState);
+ RT_ASSERT_PREEMPT_CPUID_RESTORE(pState);
+
+ Assert(SOL_THREAD_PREEMPT >= 1);
+ if (--SOL_THREAD_PREEMPT == 0 && SOL_CPU_RUNRUN != 0)
+ kpreempt(KPREEMPT_SYNC);
+}
+
+
+RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread)
+{
+ Assert(hThread == NIL_RTTHREAD);
+ return servicing_interrupt() ? true : false;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/thread2-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/thread2-r0drv-solaris.c
new file mode 100644
index 00000000..8ff450a5
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/thread2-r0drv-solaris.c
@@ -0,0 +1,160 @@
+/* $Id: thread2-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Threads (Part 2), Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/thread.h>
+#include <iprt/process.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/thread.h"
+
+#define SOL_THREAD_ID_PTR ((uint64_t *)((char *)curthread + g_offrtSolThreadId))
+#define SOL_THREAD_LOCKP_PTR ((disp_lock_t **)((char *)curthread + g_offrtSolThreadLock))
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ return rtThreadGetByNative(RTThreadNativeSelf());
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ int iPriority;
+ disp_lock_t **ppDispLock;
+ switch (enmType)
+ {
+ case RTTHREADTYPE_INFREQUENT_POLLER: iPriority = 60; break;
+ case RTTHREADTYPE_EMULATION: iPriority = 66; break;
+ case RTTHREADTYPE_DEFAULT: iPriority = 72; break;
+ case RTTHREADTYPE_MSG_PUMP: iPriority = 78; break;
+ case RTTHREADTYPE_IO: iPriority = 84; break;
+ case RTTHREADTYPE_TIMER: iPriority = 99; break;
+ default:
+ AssertMsgFailed(("enmType=%d\n", enmType));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ Assert(curthread);
+ thread_lock(curthread);
+ thread_change_pri(curthread, iPriority, 0);
+
+ /*
+ * thread_unlock() is a macro calling disp_lock_exit() with the thread's dispatcher lock.
+ * We need to dereference the offset manually here (for S10, S11 compatibility) rather than
+ * using the macro.
+ */
+ ppDispLock = SOL_THREAD_LOCKP_PTR;
+ disp_lock_exit(*ppDispLock);
+
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+ /* There is nothing special that needs doing here, but the
+ user really better know what he's cooking. */
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread)
+{
+ thread_join(pThread->tid);
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ NOREF(pThread);
+}
+
+
+/**
+ * Native thread main function.
+ *
+ * @param pvThreadInt The thread structure.
+ */
+static void rtThreadNativeMain(void *pvThreadInt)
+{
+ PRTTHREADINT pThreadInt = (PRTTHREADINT)pvThreadInt;
+
+ AssertCompile(sizeof(kt_did_t) == sizeof(pThreadInt->tid));
+ uint64_t *pu64ThrId = SOL_THREAD_ID_PTR;
+ pThreadInt->tid = *pu64ThrId;
+ rtThreadMain(pThreadInt, RTThreadNativeSelf(), &pThreadInt->szName[0]);
+ thread_exit();
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread)
+{
+ kthread_t *pThread;
+ RT_ASSERT_PREEMPTIBLE();
+
+ pThreadInt->tid = UINT64_MAX;
+
+ pThread = thread_create(NULL, /* Stack, use base */
+ 0, /* Stack size */
+ rtThreadNativeMain, /* Thread function */
+ pThreadInt, /* Function data */
+ 0, /* Data size */
+ &p0, /* Process 0 handle */
+ TS_RUN, /* Ready to run */
+ minclsyspri /* Priority */
+ );
+ if (RT_LIKELY(pThread))
+ {
+ *pNativeThread = (RTNATIVETHREAD)pThread;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_OUT_OF_RESOURCES;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/threadctxhooks-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/threadctxhooks-r0drv-solaris.c
new file mode 100644
index 00000000..387e9356
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/threadctxhooks-r0drv-solaris.c
@@ -0,0 +1,359 @@
+/* $Id: threadctxhooks-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Thread Context Switching Hook, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * Copyright (C) 2013-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-solaris-kernel.h"
+#include "internal/iprt.h"
+
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+#include <iprt/log.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal hook object for solaris.
+ */
+typedef struct RTTHREADCTXHOOKINT
+{
+ /** Magic value (RTTHREADCTXHOOKINT_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** The thread handle (owner) for which the context-hooks are registered. */
+ RTNATIVETHREAD hOwner;
+ /** Pointer to the registered callback function. */
+ PFNRTTHREADCTXHOOK pfnCallback;
+ /** User argument passed to the callback function. */
+ void *pvUser;
+ /** Whether the hook is enabled or not. */
+ bool volatile fEnabled;
+ /** Number of references to this object. */
+ uint32_t volatile cRefs;
+} RTTHREADCTXHOOKINT;
+typedef RTTHREADCTXHOOKINT *PRTTHREADCTXHOOKINT;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates a hook handle and returns rc if not valid. */
+#define RTTHREADCTX_VALID_RETURN_RC(pThis, rc) \
+ do { \
+ AssertPtrReturn((pThis), (rc)); \
+ AssertReturn((pThis)->u32Magic == RTTHREADCTXHOOKINT_MAGIC, (rc)); \
+ AssertReturn((pThis)->cRefs > 0, (rc)); \
+ } while (0)
+
+
+/**
+ * Hook function for the thread-save event.
+ *
+ * @param pvThreadCtxInt Opaque pointer to the internal hook object.
+ *
+ * @remarks Called with the with preemption disabled!
+ */
+static void rtThreadCtxHookSolOut(void *pvThreadCtxInt)
+{
+ PRTTHREADCTXHOOKINT pThis = (PRTTHREADCTXHOOKINT)pvThreadCtxInt;
+ AssertPtr(pThis);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ Assert(pThis->cRefs > 0);
+
+ if (pThis->fEnabled)
+ {
+ Assert(pThis->pfnCallback);
+ pThis->pfnCallback(RTTHREADCTXEVENT_OUT, pThis->pvUser);
+ }
+}
+
+
+/**
+ * Hook function for the thread-restore event.
+ *
+ * @param pvThreadCtxInt Opaque pointer to the internal hook object.
+ *
+ * @remarks Called with preemption disabled!
+ */
+static void rtThreadCtxHookSolIn(void *pvThreadCtxInt)
+{
+ PRTTHREADCTXHOOKINT pThis = (PRTTHREADCTXHOOKINT)pvThreadCtxInt;
+ AssertPtr(pThis);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ Assert(pThis->cRefs > 0);
+
+ if (pThis->fEnabled)
+ {
+ Assert(pThis->pfnCallback);
+ pThis->pfnCallback(RTTHREADCTXEVENT_IN, pThis->pvUser);
+ }
+}
+
+
+/**
+ * Hook function for the thread-free event.
+ *
+ * This is used for making sure the hook object is safely released - see
+ * RTThreadCtxHookRelease for details.
+ *
+ * @param pvThreadCtxInt Opaque pointer to the internal hook object.
+ * @param fIsExec Whether this event is triggered due to exec().
+ */
+static void rtThreadCtxHookSolFree(void *pvThreadCtxInt, int fIsExec)
+{
+ PRTTHREADCTXHOOKINT pThis = (PRTTHREADCTXHOOKINT)pvThreadCtxInt;
+ AssertPtrReturnVoid(pThis);
+ AssertMsgReturnVoid(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis));
+
+ uint32_t cRefs = ASMAtomicReadU32(&pThis->cRefs);
+ if (cRefs > 0)
+ {
+ cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if (!cRefs)
+ {
+ Assert(!pThis->fEnabled);
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
+ RTMemFree(pThis);
+ }
+ }
+ else
+ {
+ /* Should never happen. */
+ AssertMsgFailed(("rtThreadCtxHookSolFree with cRefs=0 pThis=%p\n", pThis));
+ }
+}
+
+
+RTDECL(int) RTThreadCtxHookCreate(PRTTHREADCTXHOOK phCtxHook, uint32_t fFlags, PFNRTTHREADCTXHOOK pfnCallback, void *pvUser)
+{
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis;
+ Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
+ AssertReturn(fFlags == 0, VERR_INVALID_FLAGS);
+
+ /*
+ * Allocate and initialize a new hook.
+ */
+ pThis = (PRTTHREADCTXHOOKINT)RTMemAllocZ(sizeof(*pThis));
+ if (RT_UNLIKELY(!pThis))
+ return VERR_NO_MEMORY;
+ pThis->u32Magic = RTTHREADCTXHOOKINT_MAGIC;
+ pThis->hOwner = RTThreadNativeSelf();
+ pThis->pfnCallback = pfnCallback;
+ pThis->pvUser = pvUser;
+ pThis->fEnabled = false;
+ pThis->cRefs = 2; /* One reference for the thread, one for the caller. */
+
+ /*
+ * installctx() allocates memory and thus cannot be used in RTThreadCtxHookRegister() which can be used
+ * with preemption disabled. We allocate the context-hooks here and use 'fEnabled' to determine if we can
+ * invoke the consumer's hook or not.
+ */
+ if (g_frtSolOldThreadCtx)
+ {
+ g_rtSolThreadCtx.Install.pfnSol_installctx_old(curthread,
+ pThis,
+ rtThreadCtxHookSolOut, /* save */
+ rtThreadCtxHookSolIn, /* restore */
+ NULL, /* fork */
+ NULL, /* lwp_create */
+ rtThreadCtxHookSolFree);
+ }
+ else
+ {
+ g_rtSolThreadCtx.Install.pfnSol_installctx(curthread,
+ pThis,
+ rtThreadCtxHookSolOut, /* save */
+ rtThreadCtxHookSolIn, /* restore */
+ NULL, /* fork */
+ NULL, /* lwp_create */
+ NULL, /* exit */
+ rtThreadCtxHookSolFree);
+ }
+
+ *phCtxHook = pThis;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTThreadCtxHookDestroy(RTTHREADCTXHOOK hCtxHook)
+{
+ /*
+ * Validate input, ignoring NIL.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ if (pThis == NIL_RTTHREADCTXHOOK)
+ return VINF_SUCCESS;
+ RTTHREADCTX_VALID_RETURN_RC(hCtxHook, VERR_INVALID_HANDLE);
+ Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ Assert(!pThis->fEnabled || pThis->hOwner == RTThreadNativeSelf());
+
+ /*
+ * Make sure it's disabled.
+ */
+ ASMAtomicWriteBool(&pThis->fEnabled, false);
+
+ /*
+ * Decrement.
+ */
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ if ( cRefs == 1
+ && pThis->hOwner == RTThreadNativeSelf())
+ {
+ /*
+ * removectx() will invoke rtThreadCtxHookSolFree() and there is no way to bypass it and still use
+ * rtThreadCtxHookSolFree() at the same time. Hence the convulated reference counting.
+ *
+ * When this function is called from the owner thread and is the last reference, we call removectx() which
+ * will invoke rtThreadCtxHookSolFree() with cRefs = 1 and that will then free the hook object.
+ *
+ * When the function is called from a different thread, we simply decrement the reference. Whenever the
+ * ring-0 thread dies, Solaris will call rtThreadCtxHookSolFree() which will free the hook object.
+ */
+ int rc;
+ if (g_frtSolOldThreadCtx)
+ {
+ rc = g_rtSolThreadCtx.Remove.pfnSol_removectx_old(curthread,
+ pThis,
+ rtThreadCtxHookSolOut, /* save */
+ rtThreadCtxHookSolIn, /* restore */
+ NULL, /* fork */
+ NULL, /* lwp_create */
+ rtThreadCtxHookSolFree);
+ }
+ else
+ {
+ rc = g_rtSolThreadCtx.Remove.pfnSol_removectx(curthread,
+ pThis,
+ rtThreadCtxHookSolOut, /* save */
+ rtThreadCtxHookSolIn, /* restore */
+ NULL, /* fork */
+ NULL, /* lwp_create */
+ NULL, /* exit */
+ rtThreadCtxHookSolFree);
+ }
+ AssertMsg(rc, ("removectx() failed. rc=%d\n", rc));
+ NOREF(rc);
+
+#if 0 /*def RT_STRICT - access after free */
+ cRefs = ASMAtomicReadU32(&pThis->cRefs);
+ Assert(!cRefs);
+#endif
+ cRefs = 0;
+ }
+ else if (!cRefs)
+ {
+ /*
+ * The ring-0 thread for this hook object has already died. Free up the object as we have no more references.
+ */
+ Assert(pThis->hOwner != RTThreadNativeSelf());
+ ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXHOOKINT_MAGIC);
+ RTMemFree(pThis);
+ }
+
+ return cRefs;
+}
+
+
+RTDECL(int) RTThreadCtxHookEnable(RTTHREADCTXHOOK hCtxHook)
+{
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ AssertPtr(pThis);
+ AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
+ VERR_INVALID_HANDLE);
+ Assert(pThis->hOwner == RTThreadNativeSelf());
+ Assert(!pThis->fEnabled);
+
+ /*
+ * Mark it as enabled.
+ */
+ pThis->fEnabled = true;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTThreadCtxHookDisable(RTTHREADCTXHOOK hCtxHook)
+{
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ if (pThis != NIL_RTTHREADCTXHOOK)
+ {
+ AssertPtr(pThis);
+ AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
+ VERR_INVALID_HANDLE);
+ Assert(pThis->hOwner == RTThreadNativeSelf());
+
+ /*
+ * Mark it as disabled.
+ */
+ pThis->fEnabled = false;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(bool) RTThreadCtxHookIsEnabled(RTTHREADCTXHOOK hCtxHook)
+{
+ /*
+ * Validate input.
+ */
+ PRTTHREADCTXHOOKINT pThis = hCtxHook;
+ if (pThis == NIL_RTTHREADCTXHOOK)
+ return false;
+ AssertPtr(pThis);
+ AssertMsgReturn(pThis->u32Magic == RTTHREADCTXHOOKINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis),
+ false);
+
+ return pThis->fEnabled;
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/time-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/time-r0drv-solaris.c
new file mode 100644
index 00000000..81001b97
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/time-r0drv-solaris.c
@@ -0,0 +1,80 @@
+/* $Id: time-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Time, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 RTTIME_INCL_TIMESPEC
+#include "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/time.h>
+
+
+RTDECL(uint64_t) RTTimeNanoTS(void)
+{
+ return (uint64_t)gethrtime();
+}
+
+
+RTDECL(uint64_t) RTTimeMilliTS(void)
+{
+ return RTTimeNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return RTTimeNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return RTTimeNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ timestruc_t TimeSpec;
+
+ mutex_enter(&tod_lock);
+ TimeSpec = tod_get();
+ mutex_exit(&tod_lock);
+ return RTTimeSpecSetNano(pTime, (uint64_t)TimeSpec.tv_sec * RT_NS_1SEC + TimeSpec.tv_nsec);
+}
+
diff --git a/src/VBox/Runtime/r0drv/solaris/timer-r0drv-solaris.c b/src/VBox/Runtime/r0drv/solaris/timer-r0drv-solaris.c
new file mode 100644
index 00000000..b415201d
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/solaris/timer-r0drv-solaris.c
@@ -0,0 +1,660 @@
+/* $Id: timer-r0drv-solaris.c $ */
+/** @file
+ * IPRT - Timer, Ring-0 Driver, Solaris.
+ */
+
+/*
+ * 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 "the-solaris-kernel.h"
+#include "internal/iprt.h"
+#include <iprt/timer.h>
+
+#include <iprt/asm.h>
+#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/spinlock.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a Solaris timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Flag indicating that the timer is suspended (hCyclicId should be
+ * CYCLIC_NONE). */
+ bool volatile fSuspended;
+ /** Flag indicating that the timer was suspended from the timer callback and
+ * therefore the hCyclicId may still be valid. */
+ bool volatile fSuspendedFromTimer;
+ /** Flag indicating that the timer interval was changed and that it requires
+ * manual expiration time programming for each callout. */
+ bool volatile fIntervalChanged;
+ /** Whether the timer must run on all CPUs or not. */
+ uint8_t fAllCpus;
+ /** Whether the timer must run on a specific CPU or not. */
+ uint8_t fSpecificCpu;
+ /** The CPU it must run on if fSpecificCpu is set. */
+ uint32_t iCpu;
+ /** The nano second interval for repeating timers. */
+ uint64_t volatile cNsInterval;
+ /** Cyclic timer Id. This is CYCLIC_NONE if no active timer.
+ * @remarks Please keep in mind that cyclic may call us back before the
+ * cyclic_add/cyclic_add_omni functions returns, so don't use this
+ * unguarded with cyclic_reprogram. */
+ cyclic_id_t hCyclicId;
+ /** The user callback. */
+ PFNRTTIMER pfnTimer;
+ /** The argument for the user callback. */
+ void *pvUser;
+ /** Union with timer type specific data. */
+ union
+ {
+ /** Single timer (fAllCpus == false). */
+ struct
+ {
+ /** Timer ticks. */
+ uint64_t u64Tick;
+ /** The next tick when fIntervalChanged is true, otherwise 0. */
+ uint64_t nsNextTick;
+ /** The (interrupt) thread currently active in the callback. */
+ kthread_t * volatile pActiveThread;
+ } Single;
+
+ /** Omni timer (fAllCpus == true). */
+ struct
+ {
+ /** Absolute timestamp of when the timer should fire first when starting up. */
+ uint64_t u64When;
+ /** Array of per CPU data (variable size). */
+ struct
+ {
+ /** Timer ticks (reinitialized when online'd). */
+ uint64_t u64Tick;
+ /** The (interrupt) thread currently active in the callback. */
+ kthread_t * volatile pActiveThread;
+ /** The next tick when fIntervalChanged is true, otherwise 0. */
+ uint64_t nsNextTick;
+ } aPerCpu[1];
+ } Omni;
+ } u;
+} RTTIMER;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Validates that the timer is valid. */
+#define RTTIMER_ASSERT_VALID_RET(pTimer) \
+ do \
+ { \
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE); \
+ AssertMsgReturn((pTimer)->u32Magic == RTTIMER_MAGIC, ("pTimer=%p u32Magic=%x expected %x\n", (pTimer), (pTimer)->u32Magic, RTTIMER_MAGIC), \
+ VERR_INVALID_HANDLE); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtTimerSolSingleCallbackWrapper(void *pvArg);
+static void rtTimerSolStopIt(PRTTIMER pTimer);
+
+
+/**
+ * Retains a reference to the timer.
+ *
+ * @returns New reference counter value.
+ * @param pTimer The timer.
+ */
+DECLINLINE(uint32_t) rtTimerSolRetain(PRTTIMER pTimer)
+{
+ return ASMAtomicIncU32(&pTimer->cRefs);
+}
+
+
+/**
+ * Destroys the timer when the reference counter has reached zero.
+ *
+ * @returns 0 (new references counter value).
+ * @param pTimer The timer.
+ */
+static uint32_t rtTimeSolReleaseCleanup(PRTTIMER pTimer)
+{
+ Assert(pTimer->hCyclicId == CYCLIC_NONE);
+ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+ RTMemFree(pTimer);
+ return 0;
+}
+
+
+/**
+ * Releases a reference to the timer.
+ *
+ * @returns New reference counter value.
+ * @param pTimer The timer.
+ */
+DECLINLINE(uint32_t) rtTimerSolRelease(PRTTIMER pTimer)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pTimer->cRefs);
+ if (!cRefs)
+ return rtTimeSolReleaseCleanup(pTimer);
+ return cRefs;
+}
+
+
+/**
+ * Callback wrapper for single-CPU timers.
+ *
+ * @param pvArg Opaque pointer to the timer.
+ *
+ * @remarks This will be executed in interrupt context but only at the specified
+ * level i.e. CY_LOCK_LEVEL in our case. We -CANNOT- call into the
+ * cyclic subsystem here, neither should pfnTimer().
+ */
+static void rtTimerSolSingleCallbackWrapper(void *pvArg)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvArg;
+ AssertPtrReturnVoid(pTimer);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ Assert(!pTimer->fAllCpus);
+
+ /* Make sure one-shots do not fire another time. */
+ Assert( !pTimer->fSuspended
+ || pTimer->cNsInterval != 0);
+
+ if (!pTimer->fSuspendedFromTimer)
+ {
+ /* Make sure we are firing on the right CPU. */
+ Assert( !pTimer->fSpecificCpu
+ || pTimer->iCpu == RTMpCpuId());
+
+ /* For one-shot, we may allow the callback to restart them. */
+ if (pTimer->cNsInterval == 0)
+ pTimer->fSuspendedFromTimer = true;
+
+ /*
+ * Perform the callout.
+ */
+ pTimer->u.Single.pActiveThread = curthread;
+
+ uint64_t u64Tick = ++pTimer->u.Single.u64Tick;
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, u64Tick);
+
+ pTimer->u.Single.pActiveThread = NULL;
+
+ if (RT_LIKELY(!pTimer->fSuspendedFromTimer))
+ {
+ if ( !pTimer->fIntervalChanged
+ || RT_UNLIKELY(pTimer->hCyclicId == CYCLIC_NONE))
+ return;
+
+ /*
+ * The interval was changed, we need to set the expiration time
+ * ourselves before returning. This comes at a slight cost,
+ * which is why we don't do it all the time.
+ */
+ if (pTimer->u.Single.nsNextTick)
+ pTimer->u.Single.nsNextTick += ASMAtomicUoReadU64(&pTimer->cNsInterval);
+ else
+ pTimer->u.Single.nsNextTick = RTTimeSystemNanoTS() + ASMAtomicUoReadU64(&pTimer->cNsInterval);
+ cyclic_reprogram(pTimer->hCyclicId, pTimer->u.Single.nsNextTick);
+ return;
+ }
+
+ /*
+ * The timer has been suspended, set expiration time to infinitiy.
+ */
+ }
+ if (RT_LIKELY(pTimer->hCyclicId != CYCLIC_NONE))
+ cyclic_reprogram(pTimer->hCyclicId, CY_INFINITY);
+}
+
+
+/**
+ * Callback wrapper for Omni-CPU timers.
+ *
+ * @param pvArg Opaque pointer to the timer.
+ *
+ * @remarks This will be executed in interrupt context but only at the specified
+ * level i.e. CY_LOCK_LEVEL in our case. We -CANNOT- call into the
+ * cyclic subsystem here, neither should pfnTimer().
+ */
+static void rtTimerSolOmniCallbackWrapper(void *pvArg)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvArg;
+ AssertPtrReturnVoid(pTimer);
+ Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD));
+ Assert(pTimer->fAllCpus);
+
+ if (!pTimer->fSuspendedFromTimer)
+ {
+ /*
+ * Perform the callout.
+ */
+ uint32_t const iCpu = CPU->cpu_id;
+
+ pTimer->u.Omni.aPerCpu[iCpu].pActiveThread = curthread;
+ uint64_t u64Tick = ++pTimer->u.Omni.aPerCpu[iCpu].u64Tick;
+
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, u64Tick);
+
+ pTimer->u.Omni.aPerCpu[iCpu].pActiveThread = NULL;
+
+ if (RT_LIKELY(!pTimer->fSuspendedFromTimer))
+ {
+ if ( !pTimer->fIntervalChanged
+ || RT_UNLIKELY(pTimer->hCyclicId == CYCLIC_NONE))
+ return;
+
+ /*
+ * The interval was changed, we need to set the expiration time
+ * ourselves before returning. This comes at a slight cost,
+ * which is why we don't do it all the time.
+ *
+ * Note! The cyclic_reprogram call only affects the omni cyclic
+ * component for this CPU.
+ */
+ if (pTimer->u.Omni.aPerCpu[iCpu].nsNextTick)
+ pTimer->u.Omni.aPerCpu[iCpu].nsNextTick += ASMAtomicUoReadU64(&pTimer->cNsInterval);
+ else
+ pTimer->u.Omni.aPerCpu[iCpu].nsNextTick = RTTimeSystemNanoTS() + ASMAtomicUoReadU64(&pTimer->cNsInterval);
+ cyclic_reprogram(pTimer->hCyclicId, pTimer->u.Omni.aPerCpu[iCpu].nsNextTick);
+ return;
+ }
+
+ /*
+ * The timer has been suspended, set expiration time to infinitiy.
+ */
+ }
+ if (RT_LIKELY(pTimer->hCyclicId != CYCLIC_NONE))
+ cyclic_reprogram(pTimer->hCyclicId, CY_INFINITY);
+}
+
+
+/**
+ * Omni-CPU cyclic online event. This is called before the omni cycle begins to
+ * fire on the specified CPU.
+ *
+ * @param pvArg Opaque pointer to the timer.
+ * @param pCpu Pointer to the CPU on which it will fire.
+ * @param pCyclicHandler Pointer to a cyclic handler to add to the CPU
+ * specified in @a pCpu.
+ * @param pCyclicTime Pointer to the cyclic time and interval object.
+ *
+ * @remarks We -CANNOT- call back into the cyclic subsystem here, we can however
+ * block (sleep).
+ */
+static void rtTimerSolOmniCpuOnline(void *pvArg, cpu_t *pCpu, cyc_handler_t *pCyclicHandler, cyc_time_t *pCyclicTime)
+{
+ PRTTIMER pTimer = (PRTTIMER)pvArg;
+ AssertPtrReturnVoid(pTimer);
+ AssertPtrReturnVoid(pCpu);
+ AssertPtrReturnVoid(pCyclicHandler);
+ AssertPtrReturnVoid(pCyclicTime);
+ uint32_t const iCpu = pCpu->cpu_id; /* Note! CPU is not necessarily the same as pCpu. */
+
+ pTimer->u.Omni.aPerCpu[iCpu].u64Tick = 0;
+ pTimer->u.Omni.aPerCpu[iCpu].nsNextTick = 0;
+
+ pCyclicHandler->cyh_func = (cyc_func_t)rtTimerSolOmniCallbackWrapper;
+ pCyclicHandler->cyh_arg = pTimer;
+ pCyclicHandler->cyh_level = CY_LOCK_LEVEL;
+
+ uint64_t u64Now = RTTimeSystemNanoTS();
+ if (pTimer->u.Omni.u64When < u64Now)
+ pCyclicTime->cyt_when = u64Now + pTimer->cNsInterval / 2;
+ else
+ pCyclicTime->cyt_when = pTimer->u.Omni.u64When;
+
+ pCyclicTime->cyt_interval = pTimer->cNsInterval;
+}
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ RT_ASSERT_PREEMPTIBLE();
+ *ppTimer = NULL;
+
+ /*
+ * Validate flags.
+ */
+ if (!RTTIMER_FLAGS_ARE_VALID(fFlags))
+ return VERR_INVALID_PARAMETER;
+
+ if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL
+ && !RTMpIsCpuPossible(RTMpCpuIdFromSetIndex(fFlags & RTTIMER_FLAGS_CPU_MASK)))
+ return VERR_CPU_NOT_FOUND;
+
+ /* One-shot omni timers are not supported by the cyclic system. */
+ if ( (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL
+ && u64NanoInterval == 0)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Allocate and initialize the timer handle. The omni variant has a
+ * variable sized array of ticks counts, thus the size calculation.
+ */
+ PRTTIMER pTimer = (PRTTIMER)RTMemAllocZ( (fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL
+ ? RT_UOFFSETOF_DYN(RTTIMER, u.Omni.aPerCpu[RTMpGetCount()])
+ : sizeof(RTTIMER));
+ if (!pTimer)
+ return VERR_NO_MEMORY;
+
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->cRefs = 1;
+ pTimer->fSuspended = true;
+ pTimer->fSuspendedFromTimer = false;
+ pTimer->fIntervalChanged = false;
+ if ((fFlags & RTTIMER_FLAGS_CPU_ALL) == RTTIMER_FLAGS_CPU_ALL)
+ {
+ pTimer->fAllCpus = true;
+ pTimer->fSpecificCpu = false;
+ pTimer->iCpu = UINT32_MAX;
+ }
+ else if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ {
+ pTimer->fAllCpus = false;
+ pTimer->fSpecificCpu = true;
+ pTimer->iCpu = fFlags & RTTIMER_FLAGS_CPU_MASK; /* ASSUMES: index == cpuid */
+ }
+ else
+ {
+ pTimer->fAllCpus = false;
+ pTimer->fSpecificCpu = false;
+ pTimer->iCpu = UINT32_MAX;
+ }
+ pTimer->cNsInterval = u64NanoInterval;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->hCyclicId = CYCLIC_NONE;
+
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if the calling thread is currently executing the timer proceduce for
+ * the given timer.
+ *
+ * @returns true if it is, false if it isn't.
+ * @param pTimer The timer in question.
+ */
+DECLINLINE(bool) rtTimerSolIsCallingFromTimerProc(PRTTIMER pTimer)
+{
+ kthread_t *pCurThread = curthread;
+ AssertReturn(pCurThread, false); /* serious paranoia */
+
+ if (!pTimer->fAllCpus)
+ return pTimer->u.Single.pActiveThread == pCurThread;
+ return pTimer->u.Omni.aPerCpu[CPU->cpu_id].pActiveThread == pCurThread;
+}
+
+
+RTDECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ if (pTimer == NULL)
+ return VINF_SUCCESS;
+ RTTIMER_ASSERT_VALID_RET(pTimer);
+ RT_ASSERT_INTS_ON();
+
+ /*
+ * It is not possible to destroy a timer from it's callback function.
+ * Cyclic makes that impossible (or at least extremely risky).
+ */
+ AssertReturn(!rtTimerSolIsCallingFromTimerProc(pTimer), VERR_INVALID_CONTEXT);
+
+ /*
+ * Invalidate the handle, make sure it's stopped and free the associated resources.
+ */
+ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+
+ if ( !pTimer->fSuspended
+ || pTimer->hCyclicId != CYCLIC_NONE) /* 2nd check shouldn't happen */
+ rtTimerSolStopIt(pTimer);
+
+ rtTimerSolRelease(pTimer);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ RTTIMER_ASSERT_VALID_RET(pTimer);
+ RT_ASSERT_INTS_ON();
+
+ /*
+ * It's not possible to restart a one-shot time from it's callback function,
+ * at least not at the moment.
+ */
+ AssertReturn(!rtTimerSolIsCallingFromTimerProc(pTimer), VERR_INVALID_CONTEXT);
+
+ mutex_enter(&cpu_lock);
+
+ /*
+ * Make sure it's not active already. If it was suspended from a timer
+ * callback function, we need to do some cleanup work here before we can
+ * restart the timer.
+ */
+ if (!pTimer->fSuspended)
+ {
+ if (!pTimer->fSuspendedFromTimer)
+ {
+ mutex_exit(&cpu_lock);
+ return VERR_TIMER_ACTIVE;
+ }
+ cyclic_remove(pTimer->hCyclicId);
+ pTimer->hCyclicId = CYCLIC_NONE;
+ }
+
+ pTimer->fSuspended = false;
+ pTimer->fSuspendedFromTimer = false;
+ pTimer->fIntervalChanged = false;
+ if (pTimer->fAllCpus)
+ {
+ /*
+ * Setup omni (all CPU) timer. The Omni-CPU online event will fire
+ * and from there we setup periodic timers per CPU.
+ */
+ pTimer->u.Omni.u64When = RTTimeSystemNanoTS() + (u64First ? u64First : pTimer->cNsInterval);
+
+ cyc_omni_handler_t HandlerOmni;
+ HandlerOmni.cyo_online = rtTimerSolOmniCpuOnline;
+ HandlerOmni.cyo_offline = NULL;
+ HandlerOmni.cyo_arg = pTimer;
+
+ pTimer->hCyclicId = cyclic_add_omni(&HandlerOmni);
+ }
+ else
+ {
+ cyc_handler_t Handler;
+ cyc_time_t FireTime;
+
+ /*
+ * Setup a single CPU timer. If a specific CPU was requested, it
+ * must be online or the timer cannot start.
+ */
+ if ( pTimer->fSpecificCpu
+ && !RTMpIsCpuOnline(pTimer->iCpu)) /* ASSUMES: index == cpuid */
+ {
+ pTimer->fSuspended = true;
+
+ mutex_exit(&cpu_lock);
+ return VERR_CPU_OFFLINE;
+ }
+
+ Handler.cyh_func = (cyc_func_t)rtTimerSolSingleCallbackWrapper;
+ Handler.cyh_arg = pTimer;
+ Handler.cyh_level = CY_LOCK_LEVEL;
+
+ /*
+ * Use a large interval (1 hour) so that we don't get a timer-callback between
+ * cyclic_add() and cyclic_bind(). Program the correct interval once cyclic_bind() is done.
+ * See @bugref{7691#c20}.
+ */
+ if (!pTimer->fSpecificCpu)
+ FireTime.cyt_when = RTTimeSystemNanoTS() + u64First;
+ else
+ FireTime.cyt_when = RTTimeSystemNanoTS() + u64First + RT_NS_1HOUR;
+ FireTime.cyt_interval = pTimer->cNsInterval != 0
+ ? pTimer->cNsInterval
+ : CY_INFINITY /* Special value, see cyclic_fire(). */;
+ pTimer->u.Single.u64Tick = 0;
+ pTimer->u.Single.nsNextTick = 0;
+
+ pTimer->hCyclicId = cyclic_add(&Handler, &FireTime);
+ if (pTimer->fSpecificCpu)
+ {
+ cyclic_bind(pTimer->hCyclicId, cpu[pTimer->iCpu], NULL /* cpupart */);
+ cyclic_reprogram(pTimer->hCyclicId, RTTimeSystemNanoTS() + u64First);
+ }
+ }
+
+ mutex_exit(&cpu_lock);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker common for RTTimerStop and RTTimerDestroy.
+ *
+ * @param pTimer The timer to stop.
+ */
+static void rtTimerSolStopIt(PRTTIMER pTimer)
+{
+ mutex_enter(&cpu_lock);
+
+ pTimer->fSuspended = true;
+ if (pTimer->hCyclicId != CYCLIC_NONE)
+ {
+ cyclic_remove(pTimer->hCyclicId);
+ pTimer->hCyclicId = CYCLIC_NONE;
+ }
+ pTimer->fSuspendedFromTimer = false;
+
+ mutex_exit(&cpu_lock);
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ RTTIMER_ASSERT_VALID_RET(pTimer);
+ RT_ASSERT_INTS_ON();
+
+ if (pTimer->fSuspended)
+ return VERR_TIMER_SUSPENDED;
+
+ /* Trying the cpu_lock stuff and calling cyclic_remove may deadlock
+ the system, so just mark the timer as suspened and deal with it in
+ the callback wrapper function above. */
+ if (rtTimerSolIsCallingFromTimerProc(pTimer))
+ pTimer->fSuspendedFromTimer = true;
+ else
+ rtTimerSolStopIt(pTimer);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ /*
+ * Validate.
+ */
+ RTTIMER_ASSERT_VALID_RET(pTimer);
+ AssertReturn(u64NanoInterval > 0, VERR_INVALID_PARAMETER);
+ AssertReturn(u64NanoInterval < UINT64_MAX / 8, VERR_INVALID_PARAMETER);
+ AssertReturn(pTimer->cNsInterval, VERR_INVALID_STATE);
+
+ if (pTimer->fSuspended || pTimer->fSuspendedFromTimer)
+ pTimer->cNsInterval = u64NanoInterval;
+ else
+ {
+ ASMAtomicWriteU64(&pTimer->cNsInterval, u64NanoInterval);
+ ASMAtomicWriteBool(&pTimer->fIntervalChanged, true);
+
+ if ( !pTimer->fAllCpus
+ && !pTimer->u.Single.nsNextTick
+ && pTimer->hCyclicId != CYCLIC_NONE
+ && rtTimerSolIsCallingFromTimerProc(pTimer))
+ pTimer->u.Single.nsNextTick = RTTimeSystemNanoTS();
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTTimerGetSystemGranularity(void)
+{
+ return nsec_per_tick;
+}
+
+
+RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted)
+{
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(bool) RTTimerCanDoHighResolution(void)
+{
+ return true;
+}
+