summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r0drv/darwin
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/r0drv/darwin')
-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
23 files changed, 7057 insertions, 0 deletions
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);
+}
+