From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Runtime/r0drv/darwin/Makefile.kup | 0 .../darwin/RTLogWriteDebugger-r0drv-darwin.cpp | 52 + .../r0drv/darwin/RTLogWriteStdOut-r0drv-darwin.cpp | 52 + .../Runtime/r0drv/darwin/alloc-r0drv-darwin.cpp | 152 ++ .../Runtime/r0drv/darwin/assert-r0drv-darwin.cpp | 87 ++ .../r0drv/darwin/dbgkrnlinfo-r0drv-darwin.cpp | 1576 ++++++++++++++++++++ .../Runtime/r0drv/darwin/fileio-r0drv-darwin.cpp | 323 ++++ .../Runtime/r0drv/darwin/initterm-r0drv-darwin.cpp | 133 ++ .../Runtime/r0drv/darwin/memobj-r0drv-darwin.cpp | 1571 +++++++++++++++++++ .../r0drv/darwin/memuserkernel-r0drv-darwin.cpp | 128 ++ src/VBox/Runtime/r0drv/darwin/mp-r0drv-darwin.cpp | 324 ++++ .../Runtime/r0drv/darwin/process-r0drv-darwin.cpp | 56 + .../rtStrFormatKernelAddress-r0drv-darwin.cpp | 60 + .../Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp | 445 ++++++ .../r0drv/darwin/semeventmulti-r0drv-darwin.cpp | 467 ++++++ .../r0drv/darwin/semfastmutex-r0drv-darwin.cpp | 150 ++ .../Runtime/r0drv/darwin/semmutex-r0drv-darwin.cpp | 417 ++++++ .../Runtime/r0drv/darwin/spinlock-r0drv-darwin.cpp | 187 +++ src/VBox/Runtime/r0drv/darwin/the-darwin-kernel.h | 262 ++++ .../Runtime/r0drv/darwin/thread-r0drv-darwin.cpp | 92 ++ .../Runtime/r0drv/darwin/thread2-r0drv-darwin.cpp | 202 +++ .../r0drv/darwin/threadpreempt-r0drv-darwin.cpp | 213 +++ .../Runtime/r0drv/darwin/time-r0drv-darwin.cpp | 108 ++ 23 files changed, 7057 insertions(+) create mode 100644 src/VBox/Runtime/r0drv/darwin/Makefile.kup create mode 100644 src/VBox/Runtime/r0drv/darwin/RTLogWriteDebugger-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/RTLogWriteStdOut-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/alloc-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/assert-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/dbgkrnlinfo-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/fileio-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/initterm-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/memobj-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/memuserkernel-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/mp-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/process-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/rtStrFormatKernelAddress-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/semevent-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/semeventmulti-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/semfastmutex-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/semmutex-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/spinlock-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/the-darwin-kernel.h create mode 100644 src/VBox/Runtime/r0drv/darwin/thread-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/thread2-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/threadpreempt-r0drv-darwin.cpp create mode 100644 src/VBox/Runtime/r0drv/darwin/time-r0drv-darwin.cpp (limited to 'src/VBox/Runtime/r0drv/darwin') 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 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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + + +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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + + +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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 +#include + +#include +#include +#include +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#include +#include +#include + +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 +RT_C_DECLS_BEGIN /* Buggy 10.4 headers, fixed in 10.5. */ +# if MAC_OS_X_VERSION_MIN_REQUIRED < 101500 +# include +# include +# include +# endif +# include +RT_C_DECLS_END +# include +# include +# include +/*# include */ +# include +# include +/*# include */ +#else +# include /* for printf */ +#endif + +#if !defined(IN_RING0) && !defined(DOXYGEN_RUNNING) /* A linking tweak for the testcase: */ +# include +# undef RTR0DECL +# define RTR0DECL(type) DECLHIDDEN(type) RTCALL +#endif + +#include "internal/iprt.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 +#include "internal/iprt.h" + +#include +#include +#include +#include +#include +#include +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 +#include +#include +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 +#include + +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include + + +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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + + +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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 +#include + +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#include +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include +#include +#include +#include +#include + +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#include +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include +#include +#include +#include + +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#include +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include +#include + +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include +#include + +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#include +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include + +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under 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 +#include +#undef ALIGN +#undef MIN +#undef MAX +#undef PAGE_SIZE +#undef PAGE_SHIFT +#undef PVM + + +/* Include the IPRT definitions of the conflicting #defines & typedefs. */ +#include +#include +#include + + +/* 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 +#include +#include +#include +#if defined(RT_ARCH_X86) && MAC_OS_X_VERSION_MIN_REQUIRED < 1060 +# include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* 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 +#include +#include + +#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 +#include +#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 + +/** + * 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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include +#include + + + +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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86) +# include +#endif +#include +#include +#include +#include + + +/********************************************************************************************************************************* +* 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 . + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms 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 + +#include + + +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); +} + -- cgit v1.2.3