diff options
Diffstat (limited to 'src/VBox/Runtime/r0drv/freebsd')
19 files changed, 3932 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r0drv/freebsd/Makefile.kup b/src/VBox/Runtime/r0drv/freebsd/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/Makefile.kup diff --git a/src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c new file mode 100644 index 00000000..60294ed3 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/alloc-r0drv-freebsd.c @@ -0,0 +1,185 @@ +/* $Id: alloc-r0drv-freebsd.c $ */ +/** @file + * IPRT - Memory Allocation, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" +#include "internal/iprt.h" +#include <iprt/mem.h> + +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/param.h> + +#include "r0drv/alloc-r0drv.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/* These two statements will define two globals and add initializers + and destructors that will be called at load/unload time (I think). */ +MALLOC_DEFINE(M_IPRTHEAP, "iprtheap", "IPRT - heap"); +MALLOC_DEFINE(M_IPRTCONT, "iprtcont", "IPRT - contiguous"); + + +DECLHIDDEN(int) rtR0MemAllocEx(size_t cb, uint32_t fFlags, PRTMEMHDR *ppHdr) +{ + size_t cbAllocated = cb; + PRTMEMHDR pHdr = NULL; + +#ifdef RT_ARCH_AMD64 + /* + * Things are a bit more complicated on AMD64 for executable memory + * because we need to be in the ~2GB..~0 range for code. + */ + if (fFlags & RTMEMHDR_FLAG_EXEC) + { + if (fFlags & RTMEMHDR_FLAG_ANY_CTX) + return VERR_NOT_SUPPORTED; + +# ifdef USE_KMEM_ALLOC_PROT + pHdr = (PRTMEMHDR)kmem_alloc_prot(kernel_map, cb + sizeof(*pHdr), + VM_PROT_ALL, VM_PROT_ALL, KERNBASE); +# else + vm_object_t pVmObject = NULL; + vm_offset_t Addr = KERNBASE; + cbAllocated = RT_ALIGN_Z(cb + sizeof(*pHdr), PAGE_SIZE); + + pVmObject = vm_object_allocate(OBJT_DEFAULT, cbAllocated >> PAGE_SHIFT); + if (!pVmObject) + return VERR_NO_EXEC_MEMORY; + + /* Addr contains a start address vm_map_find will start searching for suitable space at. */ +#if __FreeBSD_version >= 1000055 + int rc = vm_map_find(kernel_map, pVmObject, 0, &Addr, + cbAllocated, 0, VMFS_ANY_SPACE, VM_PROT_ALL, VM_PROT_ALL, 0); +#else + int rc = vm_map_find(kernel_map, pVmObject, 0, &Addr, + cbAllocated, TRUE, VM_PROT_ALL, VM_PROT_ALL, 0); +#endif + if (rc == KERN_SUCCESS) + { + rc = vm_map_wire(kernel_map, Addr, Addr + cbAllocated, + VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); + if (rc == KERN_SUCCESS) + { + pHdr = (PRTMEMHDR)Addr; + + if (fFlags & RTMEMHDR_FLAG_ZEROED) + bzero(pHdr, cbAllocated); + } + else + vm_map_remove(kernel_map, + Addr, + Addr + cbAllocated); + } + else + vm_object_deallocate(pVmObject); +# endif + } + else +#endif + { + pHdr = (PRTMEMHDR)malloc(cb + sizeof(RTMEMHDR), M_IPRTHEAP, + fFlags & RTMEMHDR_FLAG_ZEROED ? M_NOWAIT | M_ZERO : M_NOWAIT); + } + + if (RT_UNLIKELY(!pHdr)) + return VERR_NO_MEMORY; + + pHdr->u32Magic = RTMEMHDR_MAGIC; + pHdr->fFlags = fFlags; + pHdr->cb = cbAllocated; + pHdr->cbReq = cb; + + *ppHdr = pHdr; + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtR0MemFree(PRTMEMHDR pHdr) +{ + pHdr->u32Magic += 1; + +#ifdef RT_ARCH_AMD64 + if (pHdr->fFlags & RTMEMHDR_FLAG_EXEC) +# ifdef USE_KMEM_ALLOC_PROT + kmem_free(kernel_map, (vm_offset_t)pHdr, pHdr->cb); +# else + vm_map_remove(kernel_map, (vm_offset_t)pHdr, ((vm_offset_t)pHdr) + pHdr->cb); +# endif + else +#endif + free(pHdr, M_IPRTHEAP); +} + + +RTR0DECL(void *) RTMemContAlloc(PRTCCPHYS pPhys, size_t cb) +{ + void *pv; + + /* + * Validate input. + */ + AssertPtr(pPhys); + Assert(cb > 0); + + /* + * This API works in pages, so no need to do any size aligning. + */ + pv = contigmalloc(cb, /* size */ + M_IPRTCONT, /* type */ + M_NOWAIT | M_ZERO, /* flags */ + 0, /* lowest physical address*/ + _4G-1, /* highest physical address */ + PAGE_SIZE, /* alignment. */ + 0); /* boundary */ + if (pv) + { + Assert(!((uintptr_t)pv & PAGE_OFFSET_MASK)); + *pPhys = vtophys(pv); + Assert(!(*pPhys & PAGE_OFFSET_MASK)); + } + return pv; +} + + +RTR0DECL(void) RTMemContFree(void *pv, size_t cb) +{ + if (pv) + { + AssertMsg(!((uintptr_t)pv & PAGE_OFFSET_MASK), ("pv=%p\n", pv)); + contigfree(pv, cb, M_IPRTCONT); + } +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/assert-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/assert-r0drv-freebsd.c new file mode 100644 index 00000000..b92198c9 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/assert-r0drv-freebsd.c @@ -0,0 +1,70 @@ +/* $Id: assert-r0drv-freebsd.c $ */ +/** @file + * IPRT - Assertion Workers, Ring-0 Drivers, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/string.h> +#include <iprt/stdarg.h> + +#include "internal/assert.h" + + +DECLHIDDEN(void) rtR0AssertNativeMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction) +{ + printf("\r\n!!Assertion Failed!!\r\n" + "Expression: %s\r\n" + "Location : %s(%d) %s\r\n", + pszExpr, pszFile, uLine, pszFunction); +} + + +DECLHIDDEN(void) rtR0AssertNativeMsg2V(bool fInitial, const char *pszFormat, va_list va) +{ + char szMsg[256]; + + RTStrPrintfV(szMsg, sizeof(szMsg) - 1, pszFormat, va); + szMsg[sizeof(szMsg) - 1] = '\0'; + printf("%s", szMsg); + + NOREF(fInitial); +} + + +RTR0DECL(void) RTR0AssertPanicSystem(void) +{ + /** @todo implement RTR0AssertPanicSystem. */ +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c new file mode 100644 index 00000000..8144bf15 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/initterm-r0drv-freebsd.c @@ -0,0 +1,53 @@ +/* $Id: initterm-r0drv-freebsd.c $ */ +/** @file + * IPRT - Initialization & Termination, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/errcore.h> + +#include "internal/initterm.h" + + +DECLHIDDEN(int) rtR0InitNative(void) +{ + /* nothing to do */ + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtR0TermNative(void) +{ + /* nothing to undo */ +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c new file mode 100644 index 00000000..6561e1bc --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/memobj-r0drv-freebsd.c @@ -0,0 +1,905 @@ +/* $Id: memobj-r0drv-freebsd.c $ */ +/** @file + * IPRT - Ring-0 Memory Objects, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * Copyright (c) 2011 Andriy Gapon <avg@FreeBSD.org> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/memobj.h> +#include <iprt/mem.h> +#include <iprt/err.h> +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/param.h> +#include <iprt/process.h> +#include "internal/memobj.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * The FreeBSD version of the memory object structure. + */ +typedef struct RTR0MEMOBJFREEBSD +{ + /** The core structure. */ + RTR0MEMOBJINTERNAL Core; + /** The VM object associated with the allocation. */ + vm_object_t pObject; +} RTR0MEMOBJFREEBSD, *PRTR0MEMOBJFREEBSD; + + +MALLOC_DEFINE(M_IPRTMOBJ, "iprtmobj", "IPRT - R0MemObj"); + + +/** + * Gets the virtual memory map the specified object is mapped into. + * + * @returns VM map handle on success, NULL if no map. + * @param pMem The memory object. + */ +static vm_map_t rtR0MemObjFreeBSDGetMap(PRTR0MEMOBJINTERNAL pMem) +{ + switch (pMem->enmType) + { + case RTR0MEMOBJTYPE_PAGE: + case RTR0MEMOBJTYPE_LOW: + case RTR0MEMOBJTYPE_CONT: + return kernel_map; + + case RTR0MEMOBJTYPE_PHYS: + case RTR0MEMOBJTYPE_PHYS_NC: + return NULL; /* pretend these have no mapping atm. */ + + case RTR0MEMOBJTYPE_LOCK: + return pMem->u.Lock.R0Process == NIL_RTR0PROCESS + ? kernel_map + : &((struct proc *)pMem->u.Lock.R0Process)->p_vmspace->vm_map; + + case RTR0MEMOBJTYPE_RES_VIRT: + return pMem->u.ResVirt.R0Process == NIL_RTR0PROCESS + ? kernel_map + : &((struct proc *)pMem->u.ResVirt.R0Process)->p_vmspace->vm_map; + + case RTR0MEMOBJTYPE_MAPPING: + return pMem->u.Mapping.R0Process == NIL_RTR0PROCESS + ? kernel_map + : &((struct proc *)pMem->u.Mapping.R0Process)->p_vmspace->vm_map; + + default: + return NULL; + } +} + + +DECLHIDDEN(int) rtR0MemObjNativeFree(RTR0MEMOBJ pMem) +{ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)pMem; + int rc; + + switch (pMemFreeBSD->Core.enmType) + { + case RTR0MEMOBJTYPE_PAGE: + case RTR0MEMOBJTYPE_LOW: + case RTR0MEMOBJTYPE_CONT: + rc = vm_map_remove(kernel_map, + (vm_offset_t)pMemFreeBSD->Core.pv, + (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb); + AssertMsg(rc == KERN_SUCCESS, ("%#x", rc)); + break; + + case RTR0MEMOBJTYPE_LOCK: + { + vm_map_t pMap = kernel_map; + + if (pMemFreeBSD->Core.u.Lock.R0Process != NIL_RTR0PROCESS) + pMap = &((struct proc *)pMemFreeBSD->Core.u.Lock.R0Process)->p_vmspace->vm_map; + + rc = vm_map_unwire(pMap, + (vm_offset_t)pMemFreeBSD->Core.pv, + (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb, + VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); + AssertMsg(rc == KERN_SUCCESS, ("%#x", rc)); + break; + } + + case RTR0MEMOBJTYPE_RES_VIRT: + { + vm_map_t pMap = kernel_map; + if (pMemFreeBSD->Core.u.ResVirt.R0Process != NIL_RTR0PROCESS) + pMap = &((struct proc *)pMemFreeBSD->Core.u.ResVirt.R0Process)->p_vmspace->vm_map; + rc = vm_map_remove(pMap, + (vm_offset_t)pMemFreeBSD->Core.pv, + (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb); + AssertMsg(rc == KERN_SUCCESS, ("%#x", rc)); + break; + } + + case RTR0MEMOBJTYPE_MAPPING: + { + vm_map_t pMap = kernel_map; + + if (pMemFreeBSD->Core.u.Mapping.R0Process != NIL_RTR0PROCESS) + pMap = &((struct proc *)pMemFreeBSD->Core.u.Mapping.R0Process)->p_vmspace->vm_map; + rc = vm_map_remove(pMap, + (vm_offset_t)pMemFreeBSD->Core.pv, + (vm_offset_t)pMemFreeBSD->Core.pv + pMemFreeBSD->Core.cb); + AssertMsg(rc == KERN_SUCCESS, ("%#x", rc)); + break; + } + + case RTR0MEMOBJTYPE_PHYS: + case RTR0MEMOBJTYPE_PHYS_NC: + { +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WLOCK(pMemFreeBSD->pObject); +#else + VM_OBJECT_LOCK(pMemFreeBSD->pObject); +#endif + vm_page_t pPage = vm_page_find_least(pMemFreeBSD->pObject, 0); +#if __FreeBSD_version < 1000000 + vm_page_lock_queues(); +#endif + for (vm_page_t pPage = vm_page_find_least(pMemFreeBSD->pObject, 0); + pPage != NULL; + pPage = vm_page_next(pPage)) + { + vm_page_unwire(pPage, 0); + } +#if __FreeBSD_version < 1000000 + vm_page_unlock_queues(); +#endif +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WUNLOCK(pMemFreeBSD->pObject); +#else + VM_OBJECT_UNLOCK(pMemFreeBSD->pObject); +#endif + vm_object_deallocate(pMemFreeBSD->pObject); + break; + } + + default: + AssertMsgFailed(("enmType=%d\n", pMemFreeBSD->Core.enmType)); + return VERR_INTERNAL_ERROR; + } + + return VINF_SUCCESS; +} + + +static vm_page_t rtR0MemObjFreeBSDContigPhysAllocHelper(vm_object_t pObject, vm_pindex_t iPIndex, + u_long cPages, vm_paddr_t VmPhysAddrHigh, + u_long uAlignment, bool fWire) +{ + vm_page_t pPages; + int cTries = 0; + +#if __FreeBSD_version > 1000000 + int fFlags = VM_ALLOC_INTERRUPT | VM_ALLOC_NOBUSY; + if (fWire) + fFlags |= VM_ALLOC_WIRED; + + while (cTries <= 1) + { +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WLOCK(pObject); +#else + VM_OBJECT_LOCK(pObject); +#endif + pPages = vm_page_alloc_contig(pObject, iPIndex, fFlags, cPages, 0, + VmPhysAddrHigh, uAlignment, 0, VM_MEMATTR_DEFAULT); +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WUNLOCK(pObject); +#else + VM_OBJECT_UNLOCK(pObject); +#endif + if (pPages) + break; + vm_pageout_grow_cache(cTries, 0, VmPhysAddrHigh); + cTries++; + } + + return pPages; +#else + while (cTries <= 1) + { + pPages = vm_phys_alloc_contig(cPages, 0, VmPhysAddrHigh, uAlignment, 0); + if (pPages) + break; + vm_contig_grow_cache(cTries, 0, VmPhysAddrHigh); + cTries++; + } + + if (!pPages) + return pPages; +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WLOCK(pObject); +#else + VM_OBJECT_LOCK(pObject); +#endif + for (vm_pindex_t iPage = 0; iPage < cPages; iPage++) + { + vm_page_t pPage = pPages + iPage; + vm_page_insert(pPage, pObject, iPIndex + iPage); + pPage->valid = VM_PAGE_BITS_ALL; + if (fWire) + { + pPage->wire_count = 1; + atomic_add_int(&cnt.v_wire_count, 1); + } + } +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WUNLOCK(pObject); +#else + VM_OBJECT_UNLOCK(pObject); +#endif + return pPages; +#endif +} + +static int rtR0MemObjFreeBSDPhysAllocHelper(vm_object_t pObject, u_long cPages, + vm_paddr_t VmPhysAddrHigh, u_long uAlignment, + bool fContiguous, bool fWire, int rcNoMem) +{ + if (fContiguous) + { + if (rtR0MemObjFreeBSDContigPhysAllocHelper(pObject, 0, cPages, VmPhysAddrHigh, + uAlignment, fWire) != NULL) + return VINF_SUCCESS; + return rcNoMem; + } + + for (vm_pindex_t iPage = 0; iPage < cPages; iPage++) + { + vm_page_t pPage = rtR0MemObjFreeBSDContigPhysAllocHelper(pObject, iPage, 1, VmPhysAddrHigh, + uAlignment, fWire); + if (!pPage) + { + /* Free all allocated pages */ +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WLOCK(pObject); +#else + VM_OBJECT_LOCK(pObject); +#endif + while (iPage-- > 0) + { + pPage = vm_page_lookup(pObject, iPage); +#if __FreeBSD_version < 1000000 + vm_page_lock_queues(); +#endif + if (fWire) + vm_page_unwire(pPage, 0); + vm_page_free(pPage); +#if __FreeBSD_version < 1000000 + vm_page_unlock_queues(); +#endif + } +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WUNLOCK(pObject); +#else + VM_OBJECT_UNLOCK(pObject); +#endif + return rcNoMem; + } + } + return VINF_SUCCESS; +} + +static int rtR0MemObjFreeBSDAllocHelper(PRTR0MEMOBJFREEBSD pMemFreeBSD, bool fExecutable, + vm_paddr_t VmPhysAddrHigh, bool fContiguous, int rcNoMem) +{ + vm_offset_t MapAddress = vm_map_min(kernel_map); + size_t cPages = atop(pMemFreeBSD->Core.cb); + int rc; + + pMemFreeBSD->pObject = vm_object_allocate(OBJT_PHYS, cPages); + + /* No additional object reference for auto-deallocation upon unmapping. */ +#if __FreeBSD_version >= 1000055 + rc = vm_map_find(kernel_map, pMemFreeBSD->pObject, 0, + &MapAddress, pMemFreeBSD->Core.cb, 0, VMFS_ANY_SPACE, + fExecutable ? VM_PROT_ALL : VM_PROT_RW, VM_PROT_ALL, 0); +#else + rc = vm_map_find(kernel_map, pMemFreeBSD->pObject, 0, + &MapAddress, pMemFreeBSD->Core.cb, VMFS_ANY_SPACE, + fExecutable ? VM_PROT_ALL : VM_PROT_RW, VM_PROT_ALL, 0); +#endif + + if (rc == KERN_SUCCESS) + { + rc = rtR0MemObjFreeBSDPhysAllocHelper(pMemFreeBSD->pObject, cPages, + VmPhysAddrHigh, PAGE_SIZE, fContiguous, + false, rcNoMem); + if (RT_SUCCESS(rc)) + { + vm_map_wire(kernel_map, MapAddress, MapAddress + pMemFreeBSD->Core.cb, + VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); + + /* Store start address */ + pMemFreeBSD->Core.pv = (void *)MapAddress; + return VINF_SUCCESS; + } + + vm_map_remove(kernel_map, MapAddress, MapAddress + pMemFreeBSD->Core.cb); + } + else + { + rc = rcNoMem; /** @todo fix translation (borrow from darwin) */ + vm_object_deallocate(pMemFreeBSD->pObject); + } + + rtR0MemObjDelete(&pMemFreeBSD->Core); + return rc; +} +DECLHIDDEN(int) rtR0MemObjNativeAllocPage(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable) +{ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), + RTR0MEMOBJTYPE_PAGE, NULL, cb); + if (!pMemFreeBSD) + return VERR_NO_MEMORY; + + int rc = rtR0MemObjFreeBSDAllocHelper(pMemFreeBSD, fExecutable, ~(vm_paddr_t)0, false, VERR_NO_MEMORY); + if (RT_FAILURE(rc)) + { + rtR0MemObjDelete(&pMemFreeBSD->Core); + return rc; + } + + *ppMem = &pMemFreeBSD->Core; + return rc; +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocLow(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable) +{ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), + RTR0MEMOBJTYPE_LOW, NULL, cb); + if (!pMemFreeBSD) + return VERR_NO_MEMORY; + + int rc = rtR0MemObjFreeBSDAllocHelper(pMemFreeBSD, fExecutable, _4G - 1, false, VERR_NO_LOW_MEMORY); + if (RT_FAILURE(rc)) + { + rtR0MemObjDelete(&pMemFreeBSD->Core); + return rc; + } + + *ppMem = &pMemFreeBSD->Core; + return rc; +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocCont(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, bool fExecutable) +{ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), + RTR0MEMOBJTYPE_CONT, NULL, cb); + if (!pMemFreeBSD) + return VERR_NO_MEMORY; + + int rc = rtR0MemObjFreeBSDAllocHelper(pMemFreeBSD, fExecutable, _4G - 1, true, VERR_NO_CONT_MEMORY); + if (RT_FAILURE(rc)) + { + rtR0MemObjDelete(&pMemFreeBSD->Core); + return rc; + } + + pMemFreeBSD->Core.u.Cont.Phys = vtophys(pMemFreeBSD->Core.pv); + *ppMem = &pMemFreeBSD->Core; + return rc; +} + + +static int rtR0MemObjFreeBSDAllocPhysPages(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJTYPE enmType, + size_t cb, + RTHCPHYS PhysHighest, size_t uAlignment, + bool fContiguous, int rcNoMem) +{ + uint32_t cPages = atop(cb); + vm_paddr_t VmPhysAddrHigh; + + /* create the object. */ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), + enmType, NULL, cb); + if (!pMemFreeBSD) + return VERR_NO_MEMORY; + + pMemFreeBSD->pObject = vm_object_allocate(OBJT_PHYS, atop(cb)); + + if (PhysHighest != NIL_RTHCPHYS) + VmPhysAddrHigh = PhysHighest; + else + VmPhysAddrHigh = ~(vm_paddr_t)0; + + int rc = rtR0MemObjFreeBSDPhysAllocHelper(pMemFreeBSD->pObject, cPages, VmPhysAddrHigh, + uAlignment, fContiguous, true, rcNoMem); + if (RT_SUCCESS(rc)) + { + if (fContiguous) + { + Assert(enmType == RTR0MEMOBJTYPE_PHYS); +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WLOCK(pMemFreeBSD->pObject); +#else + VM_OBJECT_LOCK(pMemFreeBSD->pObject); +#endif + pMemFreeBSD->Core.u.Phys.PhysBase = VM_PAGE_TO_PHYS(vm_page_find_least(pMemFreeBSD->pObject, 0)); +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WUNLOCK(pMemFreeBSD->pObject); +#else + VM_OBJECT_UNLOCK(pMemFreeBSD->pObject); +#endif + pMemFreeBSD->Core.u.Phys.fAllocated = true; + } + + *ppMem = &pMemFreeBSD->Core; + } + else + { + vm_object_deallocate(pMemFreeBSD->pObject); + rtR0MemObjDelete(&pMemFreeBSD->Core); + } + + return rc; +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocPhys(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment) +{ + return rtR0MemObjFreeBSDAllocPhysPages(ppMem, RTR0MEMOBJTYPE_PHYS, cb, PhysHighest, uAlignment, true, VERR_NO_MEMORY); +} + + +DECLHIDDEN(int) rtR0MemObjNativeAllocPhysNC(PPRTR0MEMOBJINTERNAL ppMem, size_t cb, RTHCPHYS PhysHighest) +{ + return rtR0MemObjFreeBSDAllocPhysPages(ppMem, RTR0MEMOBJTYPE_PHYS_NC, cb, PhysHighest, PAGE_SIZE, false, VERR_NO_PHYS_MEMORY); +} + + +DECLHIDDEN(int) rtR0MemObjNativeEnterPhys(PPRTR0MEMOBJINTERNAL ppMem, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy) +{ + AssertReturn(uCachePolicy == RTMEM_CACHE_POLICY_DONT_CARE, VERR_NOT_SUPPORTED); + + /* create the object. */ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_PHYS, NULL, cb); + if (!pMemFreeBSD) + return VERR_NO_MEMORY; + + /* there is no allocation here, it needs to be mapped somewhere first. */ + pMemFreeBSD->Core.u.Phys.fAllocated = false; + pMemFreeBSD->Core.u.Phys.PhysBase = Phys; + pMemFreeBSD->Core.u.Phys.uCachePolicy = uCachePolicy; + *ppMem = &pMemFreeBSD->Core; + return VINF_SUCCESS; +} + + +/** + * Worker locking the memory in either kernel or user maps. + */ +static int rtR0MemObjNativeLockInMap(PPRTR0MEMOBJINTERNAL ppMem, vm_map_t pVmMap, + vm_offset_t AddrStart, size_t cb, uint32_t fAccess, + RTR0PROCESS R0Process, int fFlags) +{ + int rc; + NOREF(fAccess); + + /* create the object. */ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_LOCK, (void *)AddrStart, cb); + if (!pMemFreeBSD) + return VERR_NO_MEMORY; + + /* + * We could've used vslock here, but we don't wish to be subject to + * resource usage restrictions, so we'll call vm_map_wire directly. + */ + rc = vm_map_wire(pVmMap, /* the map */ + AddrStart, /* start */ + AddrStart + cb, /* end */ + fFlags); /* flags */ + if (rc == KERN_SUCCESS) + { + pMemFreeBSD->Core.u.Lock.R0Process = R0Process; + *ppMem = &pMemFreeBSD->Core; + return VINF_SUCCESS; + } + rtR0MemObjDelete(&pMemFreeBSD->Core); + return VERR_NO_MEMORY;/** @todo fix mach -> vbox error conversion for freebsd. */ +} + + +DECLHIDDEN(int) rtR0MemObjNativeLockUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess, RTR0PROCESS R0Process) +{ + return rtR0MemObjNativeLockInMap(ppMem, + &((struct proc *)R0Process)->p_vmspace->vm_map, + (vm_offset_t)R3Ptr, + cb, + fAccess, + R0Process, + VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES); +} + + +DECLHIDDEN(int) rtR0MemObjNativeLockKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pv, size_t cb, uint32_t fAccess) +{ + return rtR0MemObjNativeLockInMap(ppMem, + kernel_map, + (vm_offset_t)pv, + cb, + fAccess, + NIL_RTR0PROCESS, + VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); +} + + +/** + * Worker for the two virtual address space reservers. + * + * We're leaning on the examples provided by mmap and vm_mmap in vm_mmap.c here. + */ +static int rtR0MemObjNativeReserveInMap(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment, RTR0PROCESS R0Process, vm_map_t pMap) +{ + int rc; + + /* + * The pvFixed address range must be within the VM space when specified. + */ + if ( pvFixed != (void *)-1 + && ( (vm_offset_t)pvFixed < vm_map_min(pMap) + || (vm_offset_t)pvFixed + cb > vm_map_max(pMap))) + return VERR_INVALID_PARAMETER; + + /* + * Check that the specified alignment is supported. + */ + if (uAlignment > PAGE_SIZE) + return VERR_NOT_SUPPORTED; + + /* + * Create the object. + */ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(*pMemFreeBSD), RTR0MEMOBJTYPE_RES_VIRT, NULL, cb); + if (!pMemFreeBSD) + return VERR_NO_MEMORY; + + vm_offset_t MapAddress = pvFixed != (void *)-1 + ? (vm_offset_t)pvFixed + : vm_map_min(pMap); + if (pvFixed != (void *)-1) + vm_map_remove(pMap, + MapAddress, + MapAddress + cb); + + rc = vm_map_find(pMap, /* map */ + NULL, /* object */ + 0, /* offset */ + &MapAddress, /* addr (IN/OUT) */ + cb, /* length */ +#if __FreeBSD_version >= 1000055 + 0, /* max addr */ +#endif + pvFixed == (void *)-1 ? VMFS_ANY_SPACE : VMFS_NO_SPACE, + /* find_space */ + VM_PROT_NONE, /* protection */ + VM_PROT_ALL, /* max(_prot) ?? */ + 0); /* cow (copy-on-write) */ + if (rc == KERN_SUCCESS) + { + if (R0Process != NIL_RTR0PROCESS) + { + rc = vm_map_inherit(pMap, + MapAddress, + MapAddress + cb, + VM_INHERIT_SHARE); + AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc)); + } + pMemFreeBSD->Core.pv = (void *)MapAddress; + pMemFreeBSD->Core.u.ResVirt.R0Process = R0Process; + *ppMem = &pMemFreeBSD->Core; + return VINF_SUCCESS; + } + + rc = VERR_NO_MEMORY; /** @todo fix translation (borrow from darwin) */ + rtR0MemObjDelete(&pMemFreeBSD->Core); + return rc; + +} + + +DECLHIDDEN(int) rtR0MemObjNativeReserveKernel(PPRTR0MEMOBJINTERNAL ppMem, void *pvFixed, size_t cb, size_t uAlignment) +{ + return rtR0MemObjNativeReserveInMap(ppMem, pvFixed, cb, uAlignment, NIL_RTR0PROCESS, kernel_map); +} + + +DECLHIDDEN(int) rtR0MemObjNativeReserveUser(PPRTR0MEMOBJINTERNAL ppMem, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment, RTR0PROCESS R0Process) +{ + return rtR0MemObjNativeReserveInMap(ppMem, (void *)R3PtrFixed, cb, uAlignment, R0Process, + &((struct proc *)R0Process)->p_vmspace->vm_map); +} + + +DECLHIDDEN(int) rtR0MemObjNativeMapKernel(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, void *pvFixed, size_t uAlignment, + unsigned fProt, size_t offSub, size_t cbSub) +{ +// AssertMsgReturn(!offSub && !cbSub, ("%#x %#x\n", offSub, cbSub), VERR_NOT_SUPPORTED); + AssertMsgReturn(pvFixed == (void *)-1, ("%p\n", pvFixed), VERR_NOT_SUPPORTED); + + /* + * Check that the specified alignment is supported. + */ + if (uAlignment > PAGE_SIZE) + return VERR_NOT_SUPPORTED; + + int rc; + PRTR0MEMOBJFREEBSD pMemToMapFreeBSD = (PRTR0MEMOBJFREEBSD)pMemToMap; + + /* calc protection */ + vm_prot_t ProtectionFlags = 0; + if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE) + ProtectionFlags = VM_PROT_NONE; + if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ) + ProtectionFlags |= VM_PROT_READ; + if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE) + ProtectionFlags |= VM_PROT_WRITE; + if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC) + ProtectionFlags |= VM_PROT_EXECUTE; + + vm_offset_t Addr = vm_map_min(kernel_map); + if (cbSub == 0) + cbSub = pMemToMap->cb - offSub; + + vm_object_reference(pMemToMapFreeBSD->pObject); + rc = vm_map_find(kernel_map, /* Map to insert the object in */ + pMemToMapFreeBSD->pObject, /* Object to map */ + offSub, /* Start offset in the object */ + &Addr, /* Start address IN/OUT */ + cbSub, /* Size of the mapping */ +#if __FreeBSD_version >= 1000055 + 0, /* Upper bound of mapping */ +#endif + VMFS_ANY_SPACE, /* Whether a suitable address should be searched for first */ + ProtectionFlags, /* protection flags */ + VM_PROT_ALL, /* Maximum protection flags */ + 0); /* copy-on-write and similar flags */ + + if (rc == KERN_SUCCESS) + { + rc = vm_map_wire(kernel_map, Addr, Addr + cbSub, VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES); + AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc)); + + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(RTR0MEMOBJFREEBSD), + RTR0MEMOBJTYPE_MAPPING, + (void *)Addr, + cbSub); + if (pMemFreeBSD) + { + Assert((vm_offset_t)pMemFreeBSD->Core.pv == Addr); + pMemFreeBSD->Core.u.Mapping.R0Process = NIL_RTR0PROCESS; + *ppMem = &pMemFreeBSD->Core; + return VINF_SUCCESS; + } + rc = vm_map_remove(kernel_map, Addr, Addr + cbSub); + AssertMsg(rc == KERN_SUCCESS, ("Deleting mapping failed\n")); + } + else + vm_object_deallocate(pMemToMapFreeBSD->pObject); + + return VERR_NO_MEMORY; +} + + +DECLHIDDEN(int) rtR0MemObjNativeMapUser(PPRTR0MEMOBJINTERNAL ppMem, RTR0MEMOBJ pMemToMap, RTR3PTR R3PtrFixed, size_t uAlignment, + unsigned fProt, RTR0PROCESS R0Process) +{ + /* + * Check for unsupported stuff. + */ + AssertMsgReturn(R0Process == RTR0ProcHandleSelf(), ("%p != %p\n", R0Process, RTR0ProcHandleSelf()), VERR_NOT_SUPPORTED); + if (uAlignment > PAGE_SIZE) + return VERR_NOT_SUPPORTED; + + int rc; + PRTR0MEMOBJFREEBSD pMemToMapFreeBSD = (PRTR0MEMOBJFREEBSD)pMemToMap; + struct proc *pProc = (struct proc *)R0Process; + struct vm_map *pProcMap = &pProc->p_vmspace->vm_map; + + /* calc protection */ + vm_prot_t ProtectionFlags = 0; + if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE) + ProtectionFlags = VM_PROT_NONE; + if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ) + ProtectionFlags |= VM_PROT_READ; + if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE) + ProtectionFlags |= VM_PROT_WRITE; + if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC) + ProtectionFlags |= VM_PROT_EXECUTE; + + /* calc mapping address */ + vm_offset_t AddrR3; + if (R3PtrFixed == (RTR3PTR)-1) + { + /** @todo is this needed?. */ + PROC_LOCK(pProc); + AddrR3 = round_page((vm_offset_t)pProc->p_vmspace->vm_daddr + lim_max(pProc, RLIMIT_DATA)); + PROC_UNLOCK(pProc); + } + else + AddrR3 = (vm_offset_t)R3PtrFixed; + + /* Insert the pObject in the map. */ + vm_object_reference(pMemToMapFreeBSD->pObject); + rc = vm_map_find(pProcMap, /* Map to insert the object in */ + pMemToMapFreeBSD->pObject, /* Object to map */ + 0, /* Start offset in the object */ + &AddrR3, /* Start address IN/OUT */ + pMemToMap->cb, /* Size of the mapping */ +#if __FreeBSD_version >= 1000055 + 0, /* Upper bound of the mapping */ +#endif + R3PtrFixed == (RTR3PTR)-1 ? VMFS_ANY_SPACE : VMFS_NO_SPACE, + /* Whether a suitable address should be searched for first */ + ProtectionFlags, /* protection flags */ + VM_PROT_ALL, /* Maximum protection flags */ + 0); /* copy-on-write and similar flags */ + + if (rc == KERN_SUCCESS) + { + rc = vm_map_wire(pProcMap, AddrR3, AddrR3 + pMemToMap->cb, VM_MAP_WIRE_USER|VM_MAP_WIRE_NOHOLES); + AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc)); + + rc = vm_map_inherit(pProcMap, AddrR3, AddrR3 + pMemToMap->cb, VM_INHERIT_SHARE); + AssertMsg(rc == KERN_SUCCESS, ("%#x\n", rc)); + + /* + * Create a mapping object for it. + */ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)rtR0MemObjNew(sizeof(RTR0MEMOBJFREEBSD), + RTR0MEMOBJTYPE_MAPPING, + (void *)AddrR3, + pMemToMap->cb); + if (pMemFreeBSD) + { + Assert((vm_offset_t)pMemFreeBSD->Core.pv == AddrR3); + pMemFreeBSD->Core.u.Mapping.R0Process = R0Process; + *ppMem = &pMemFreeBSD->Core; + return VINF_SUCCESS; + } + + rc = vm_map_remove(pProcMap, AddrR3, AddrR3 + pMemToMap->cb); + AssertMsg(rc == KERN_SUCCESS, ("Deleting mapping failed\n")); + } + else + vm_object_deallocate(pMemToMapFreeBSD->pObject); + + return VERR_NO_MEMORY; +} + + +DECLHIDDEN(int) rtR0MemObjNativeProtect(PRTR0MEMOBJINTERNAL pMem, size_t offSub, size_t cbSub, uint32_t fProt) +{ + vm_prot_t ProtectionFlags = 0; + vm_offset_t AddrStart = (uintptr_t)pMem->pv + offSub; + vm_offset_t AddrEnd = AddrStart + cbSub; + vm_map_t pVmMap = rtR0MemObjFreeBSDGetMap(pMem); + + if (!pVmMap) + return VERR_NOT_SUPPORTED; + + if ((fProt & RTMEM_PROT_NONE) == RTMEM_PROT_NONE) + ProtectionFlags = VM_PROT_NONE; + if ((fProt & RTMEM_PROT_READ) == RTMEM_PROT_READ) + ProtectionFlags |= VM_PROT_READ; + if ((fProt & RTMEM_PROT_WRITE) == RTMEM_PROT_WRITE) + ProtectionFlags |= VM_PROT_WRITE; + if ((fProt & RTMEM_PROT_EXEC) == RTMEM_PROT_EXEC) + ProtectionFlags |= VM_PROT_EXECUTE; + + int krc = vm_map_protect(pVmMap, AddrStart, AddrEnd, ProtectionFlags, FALSE); + if (krc == KERN_SUCCESS) + return VINF_SUCCESS; + + return VERR_NOT_SUPPORTED; +} + + +DECLHIDDEN(RTHCPHYS) rtR0MemObjNativeGetPagePhysAddr(PRTR0MEMOBJINTERNAL pMem, size_t iPage) +{ + PRTR0MEMOBJFREEBSD pMemFreeBSD = (PRTR0MEMOBJFREEBSD)pMem; + + switch (pMemFreeBSD->Core.enmType) + { + case RTR0MEMOBJTYPE_LOCK: + { + if ( pMemFreeBSD->Core.u.Lock.R0Process != NIL_RTR0PROCESS + && pMemFreeBSD->Core.u.Lock.R0Process != (RTR0PROCESS)curproc) + { + /* later */ + return NIL_RTHCPHYS; + } + + vm_offset_t pb = (vm_offset_t)pMemFreeBSD->Core.pv + ptoa(iPage); + + struct proc *pProc = (struct proc *)pMemFreeBSD->Core.u.Lock.R0Process; + struct vm_map *pProcMap = &pProc->p_vmspace->vm_map; + pmap_t pPhysicalMap = vm_map_pmap(pProcMap); + + return pmap_extract(pPhysicalMap, pb); + } + + case RTR0MEMOBJTYPE_MAPPING: + { + vm_offset_t pb = (vm_offset_t)pMemFreeBSD->Core.pv + ptoa(iPage); + + if (pMemFreeBSD->Core.u.Mapping.R0Process != NIL_RTR0PROCESS) + { + struct proc *pProc = (struct proc *)pMemFreeBSD->Core.u.Mapping.R0Process; + struct vm_map *pProcMap = &pProc->p_vmspace->vm_map; + pmap_t pPhysicalMap = vm_map_pmap(pProcMap); + + return pmap_extract(pPhysicalMap, pb); + } + return vtophys(pb); + } + + case RTR0MEMOBJTYPE_PAGE: + case RTR0MEMOBJTYPE_LOW: + case RTR0MEMOBJTYPE_PHYS_NC: + { + RTHCPHYS addr; +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WLOCK(pMemFreeBSD->pObject); +#else + VM_OBJECT_LOCK(pMemFreeBSD->pObject); +#endif + addr = VM_PAGE_TO_PHYS(vm_page_lookup(pMemFreeBSD->pObject, iPage)); +#if __FreeBSD_version >= 1000030 + VM_OBJECT_WUNLOCK(pMemFreeBSD->pObject); +#else + VM_OBJECT_UNLOCK(pMemFreeBSD->pObject); +#endif + return addr; + } + + case RTR0MEMOBJTYPE_PHYS: + return pMemFreeBSD->Core.u.Cont.Phys + ptoa(iPage); + + case RTR0MEMOBJTYPE_CONT: + return pMemFreeBSD->Core.u.Phys.PhysBase + ptoa(iPage); + + case RTR0MEMOBJTYPE_RES_VIRT: + default: + return NIL_RTHCPHYS; + } +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c new file mode 100644 index 00000000..a1b3d295 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/memuserkernel-r0drv-freebsd.c @@ -0,0 +1,83 @@ +/* $Id: memuserkernel-r0drv-freebsd.c $ */ +/** @file + * IPRT - User & Kernel Memory, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (C) 2009-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/mem.h> +#include <iprt/errcore.h> + + +RTR0DECL(int) RTR0MemUserCopyFrom(void *pvDst, RTR3PTR R3PtrSrc, size_t cb) +{ + int rc = copyin((const void *)R3PtrSrc, pvDst, cb); + if (RT_LIKELY(rc == 0)) + return VINF_SUCCESS; + return VERR_ACCESS_DENIED; +} + + +RTR0DECL(int) RTR0MemUserCopyTo(RTR3PTR R3PtrDst, void const *pvSrc, size_t cb) +{ + int rc = copyout(pvSrc, (void *)R3PtrDst, cb); + if (RT_LIKELY(rc == 0)) + return VINF_SUCCESS; + return VERR_ACCESS_DENIED; +} + + +RTR0DECL(bool) RTR0MemUserIsValidAddr(RTR3PTR R3Ptr) +{ + return R3Ptr < VM_MAXUSER_ADDRESS; +} + + +RTR0DECL(bool) RTR0MemKernelIsValidAddr(void *pv) +{ + return (uintptr_t)pv >= VM_MAXUSER_ADDRESS; +} + + +RTR0DECL(bool) RTR0MemAreKrnlAndUsrDifferent(void) +{ + return true; +} + + +RTR0DECL(int) RTR0MemKernelCopyFrom(void *pvDst, void const *pvSrc, size_t cb) +{ + return VERR_NOT_SUPPORTED; +} + + +RTR0DECL(int) RTR0MemKernelCopyTo(void *pvDst, void const *pvSrc, size_t cb) +{ + return VERR_NOT_SUPPORTED; +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c new file mode 100644 index 00000000..681fbbbe --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/mp-r0drv-freebsd.c @@ -0,0 +1,308 @@ +/* $Id: mp-r0drv-freebsd.c $ */ +/** @file + * IPRT - Multiprocessor, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/mp.h> +#include <iprt/err.h> +#include <iprt/asm.h> +#include <iprt/cpuset.h> +#include "r0drv/mp-r0drv.h" + + +RTDECL(RTCPUID) RTMpCpuId(void) +{ + return curcpu; +} + + +RTDECL(int) RTMpCurSetIndex(void) +{ + return curcpu; +} + + +RTDECL(int) RTMpCurSetIndexAndId(PRTCPUID pidCpu) +{ + return *pidCpu = curcpu; +} + + +RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu) +{ + return idCpu < RTCPUSET_MAX_CPUS && idCpu <= mp_maxid ? (int)idCpu : -1; +} + + +RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu) +{ + return (unsigned)iCpu <= mp_maxid ? (RTCPUID)iCpu : NIL_RTCPUID; +} + + +RTDECL(RTCPUID) RTMpGetMaxCpuId(void) +{ + return mp_maxid; +} + + +RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu) +{ + return idCpu <= mp_maxid; +} + + +RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet) +{ + RTCPUID idCpu; + + RTCpuSetEmpty(pSet); + idCpu = RTMpGetMaxCpuId(); + do + { + if (RTMpIsCpuPossible(idCpu)) + RTCpuSetAdd(pSet, idCpu); + } while (idCpu-- > 0); + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetCount(void) +{ + return mp_maxid + 1; +} + + +RTDECL(RTCPUID) RTMpGetCoreCount(void) +{ + return mp_maxid + 1; +} + +RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu) +{ + return idCpu <= mp_maxid + && !CPU_ABSENT(idCpu); +} + + +RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet) +{ + RTCPUID idCpu; + + RTCpuSetEmpty(pSet); + idCpu = RTMpGetMaxCpuId(); + do + { + if (RTMpIsCpuOnline(idCpu)) + RTCpuSetAdd(pSet, idCpu); + } while (idCpu-- > 0); + + return pSet; +} + + +RTDECL(RTCPUID) RTMpGetOnlineCount(void) +{ + return mp_ncpus; +} + + +/** + * Wrapper between the native FreeBSD per-cpu callback and PFNRTWORKER + * for the RTMpOnAll API. + * + * @param pvArg Pointer to the RTMPARGS package. + */ +static void rtmpOnAllFreeBSDWrapper(void *pvArg) +{ + PRTMPARGS pArgs = (PRTMPARGS)pvArg; + pArgs->pfnWorker(curcpu, pArgs->pvUser1, pArgs->pvUser2); +} + + +RTDECL(int) RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + RTMPARGS Args; + Args.pfnWorker = pfnWorker; + Args.pvUser1 = pvUser1; + Args.pvUser2 = pvUser2; + Args.idCpu = NIL_RTCPUID; + Args.cHits = 0; + smp_rendezvous(NULL, rtmpOnAllFreeBSDWrapper, smp_no_rendevous_barrier, &Args); + return VINF_SUCCESS; +} + + +/** + * Wrapper between the native FreeBSD per-cpu callback and PFNRTWORKER + * for the RTMpOnOthers API. + * + * @param pvArg Pointer to the RTMPARGS package. + */ +static void rtmpOnOthersFreeBSDWrapper(void *pvArg) +{ + PRTMPARGS pArgs = (PRTMPARGS)pvArg; + RTCPUID idCpu = curcpu; + if (pArgs->idCpu != idCpu) + pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2); +} + + +RTDECL(int) RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + /* Will panic if no rendezvousing cpus, so check up front. */ + if (RTMpGetOnlineCount() > 1) + { +#if __FreeBSD_version >= 900000 + cpuset_t Mask; +#elif __FreeBSD_version >= 700000 + cpumask_t Mask; +#endif + RTMPARGS Args; + + Args.pfnWorker = pfnWorker; + Args.pvUser1 = pvUser1; + Args.pvUser2 = pvUser2; + Args.idCpu = RTMpCpuId(); + Args.cHits = 0; +#if __FreeBSD_version >= 700000 +# if __FreeBSD_version >= 900000 + Mask = all_cpus; + CPU_CLR(curcpu, &Mask); +# else + Mask = ~(cpumask_t)curcpu; +# endif + smp_rendezvous_cpus(Mask, NULL, rtmpOnOthersFreeBSDWrapper, smp_no_rendevous_barrier, &Args); +#else + smp_rendezvous(NULL, rtmpOnOthersFreeBSDWrapper, NULL, &Args); +#endif + } + return VINF_SUCCESS; +} + + +/** + * Wrapper between the native FreeBSD per-cpu callback and PFNRTWORKER + * for the RTMpOnSpecific API. + * + * @param pvArg Pointer to the RTMPARGS package. + */ +static void rtmpOnSpecificFreeBSDWrapper(void *pvArg) +{ + PRTMPARGS pArgs = (PRTMPARGS)pvArg; + RTCPUID idCpu = curcpu; + if (pArgs->idCpu == idCpu) + { + pArgs->pfnWorker(idCpu, pArgs->pvUser1, pArgs->pvUser2); + ASMAtomicIncU32(&pArgs->cHits); + } +} + + +RTDECL(int) RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ +#if __FreeBSD_version >= 900000 + cpuset_t Mask; +#elif __FreeBSD_version >= 700000 + cpumask_t Mask; +#endif + RTMPARGS Args; + + /* Will panic if no rendezvousing cpus, so make sure the cpu is online. */ + if (!RTMpIsCpuOnline(idCpu)) + return VERR_CPU_NOT_FOUND; + + Args.pfnWorker = pfnWorker; + Args.pvUser1 = pvUser1; + Args.pvUser2 = pvUser2; + Args.idCpu = idCpu; + Args.cHits = 0; +#if __FreeBSD_version >= 700000 +# if __FreeBSD_version >= 900000 + CPU_SETOF(idCpu, &Mask); +# else + Mask = (cpumask_t)1 << idCpu; +# endif + smp_rendezvous_cpus(Mask, NULL, rtmpOnSpecificFreeBSDWrapper, smp_no_rendevous_barrier, &Args); +#else + smp_rendezvous(NULL, rtmpOnSpecificFreeBSDWrapper, NULL, &Args); +#endif + return Args.cHits == 1 + ? VINF_SUCCESS + : VERR_CPU_NOT_FOUND; +} + + +#if __FreeBSD_version >= 700000 +/** + * Dummy callback for RTMpPokeCpu. + * @param pvArg Ignored + */ +static void rtmpFreeBSDPokeCallback(void *pvArg) +{ + NOREF(pvArg); +} + + +RTDECL(int) RTMpPokeCpu(RTCPUID idCpu) +{ +#if __FreeBSD_version >= 900000 + cpuset_t Mask; +#elif __FreeBSD_version >= 700000 + cpumask_t Mask; +#endif + + /* Will panic if no rendezvousing cpus, so make sure the cpu is online. */ + if (!RTMpIsCpuOnline(idCpu)) + return VERR_CPU_NOT_FOUND; + +# if __FreeBSD_version >= 900000 + CPU_SETOF(idCpu, &Mask); +# else + Mask = (cpumask_t)1 << idCpu; +# endif + smp_rendezvous_cpus(Mask, NULL, rtmpFreeBSDPokeCallback, smp_no_rendevous_barrier, NULL); + + return VINF_SUCCESS; +} + +#else /* < 7.0 */ +RTDECL(int) RTMpPokeCpu(RTCPUID idCpu) +{ + return VERR_NOT_SUPPORTED; +} +#endif /* < 7.0 */ + + +RTDECL(bool) RTMpOnAllIsConcurrentSafe(void) +{ + return true; +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c new file mode 100644 index 00000000..02ecf25f --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/process-r0drv-freebsd.c @@ -0,0 +1,51 @@ +/* $Id: process-r0drv-freebsd.c $ */ +/** @file + * IPRT - Process Management, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/process.h> + + +RTDECL(RTPROCESS) RTProcSelf(void) +{ + struct proc *pSelf = curproc; + return pSelf->p_pid; +} + + +RTR0DECL(RTR0PROCESS) RTR0ProcHandleSelf(void) +{ + return (RTR0PROCESS)curproc; +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c new file mode 100644 index 00000000..f3635f92 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/semevent-r0drv-freebsd.c @@ -0,0 +1,256 @@ +/* $Id: semevent-r0drv-freebsd.c $ */ +/** @file + * IPRT - Single Release Event Semaphores, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMEVENT_WITHOUT_REMAPPING +#include "the-freebsd-kernel.h" +#include "internal/iprt.h" +#include <iprt/semaphore.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/lockvalidator.h> +#include <iprt/mem.h> + +#include "sleepqueue-r0drv-freebsd.h" +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * FreeBSD event semaphore. + */ +typedef struct RTSEMEVENTINTERNAL +{ + /** Magic value (RTSEMEVENT_MAGIC). */ + uint32_t volatile u32Magic; + /** The object status - !0 when signaled and 0 when reset. */ + uint32_t volatile fState; + /** Reference counter. */ + uint32_t volatile cRefs; +} RTSEMEVENTINTERNAL, *PRTSEMEVENTINTERNAL; + + +RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem) +{ + return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...) +{ + AssertCompile(sizeof(RTSEMEVENTINTERNAL) > sizeof(void *)); + AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER); + Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL)); + AssertPtrReturn(phEventSem, VERR_INVALID_POINTER); + + PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)RTMemAllocZ(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + pThis->u32Magic = RTSEMEVENT_MAGIC; + pThis->cRefs = 1; + pThis->fState = 0; + + *phEventSem = pThis; + return VINF_SUCCESS; +} + + +/** + * Retains a reference to the event semaphore. + * + * @param pThis The event semaphore. + */ +DECLINLINE(void) rtR0SemEventBsdRetain(PRTSEMEVENTINTERNAL pThis) +{ + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs < 100000); NOREF(cRefs); +} + + +/** + * Releases a reference to the event semaphore. + * + * @param pThis The event semaphore. + */ +DECLINLINE(void) rtR0SemEventBsdRelease(PRTSEMEVENTINTERNAL pThis) +{ + if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0)) + RTMemFree(pThis); +} + + +RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTINTERNAL pThis = hEventSem; + if (pThis == NIL_RTSEMEVENT) + return VINF_SUCCESS; + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); + Assert(pThis->cRefs > 0); + + /* + * Invalidate it and signal the object just in case. + */ + ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC); + ASMAtomicWriteU32(&pThis->fState, 0); + rtR0SemBsdBroadcast(pThis); + rtR0SemEventBsdRelease(pThis); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTINTERNAL pThis = (PRTSEMEVENTINTERNAL)hEventSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); + rtR0SemEventBsdRetain(pThis); + + /* + * Signal the event object. + */ + ASMAtomicWriteU32(&pThis->fState, 1); + rtR0SemBsdSignal(pThis); + rtR0SemEventBsdRelease(pThis); + return VINF_SUCCESS; +} + +/** + * Worker for RTSemEventWaitEx and RTSemEventWaitExDebug. + * + * @returns VBox status code. + * @param pThis The event semaphore. + * @param fFlags See RTSemEventWaitEx. + * @param uTimeout See RTSemEventWaitEx. + * @param pSrcPos The source code position of the wait. + */ +static int rtR0SemEventWait(PRTSEMEVENTINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout, + PCRTLOCKVALSRCPOS pSrcPos) +{ + int rc; + + /* + * Validate the input. + */ + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER); + rtR0SemEventBsdRetain(pThis); + + /* + * Try grab the event without setting up the wait. + */ + if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1)) + rc = VINF_SUCCESS; + else + { + /* + * We have to wait. + */ + RTR0SEMBSDSLEEP Wait; + rc = rtR0SemBsdWaitInit(&Wait, fFlags, uTimeout, pThis); + if (RT_SUCCESS(rc)) + { + for (;;) + { + /* The destruction test. */ + if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC)) + rc = VERR_SEM_DESTROYED; + else + { + rtR0SemBsdWaitPrepare(&Wait); + + /* Check the exit conditions. */ + if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENT_MAGIC)) + rc = VERR_SEM_DESTROYED; + else if (ASMAtomicCmpXchgU32(&pThis->fState, 0, 1)) + rc = VINF_SUCCESS; + else if (rtR0SemBsdWaitHasTimedOut(&Wait)) + rc = VERR_TIMEOUT; + else if (rtR0SemBsdWaitWasInterrupted(&Wait)) + rc = VERR_INTERRUPTED; + else + { + /* Do the wait and then recheck the conditions. */ + rtR0SemBsdWaitDoIt(&Wait); + continue; + } + } + break; + } + + rtR0SemBsdWaitDelete(&Wait); + } + } + + rtR0SemEventBsdRelease(pThis); + return rc; +} + + +RTDECL(int) RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout) +{ +#ifndef RTSEMEVENT_STRICT + return rtR0SemEventWait(hEventSem, fFlags, uTimeout, NULL); +#else + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); + return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos); +#endif +} +RT_EXPORT_SYMBOL(RTSemEventWaitEx); + + +RTDECL(int) RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); + return rtR0SemEventWait(hEventSem, fFlags, uTimeout, &SrcPos); +} +RT_EXPORT_SYMBOL(RTSemEventWaitExDebug); + + +RTDECL(uint32_t) RTSemEventGetResolution(void) +{ + return 1000000000 / hz; +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c new file mode 100644 index 00000000..15609046 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/semeventmulti-r0drv-freebsd.c @@ -0,0 +1,320 @@ +/* $Id: semeventmulti-r0drv-freebsd.c $ */ +/** @file + * IPRT - Multiple Release Event Semaphores, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMEVENTMULTI_WITHOUT_REMAPPING +#include "the-freebsd-kernel.h" +#include "internal/iprt.h" +#include <iprt/semaphore.h> + +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/lockvalidator.h> + +#include "sleepqueue-r0drv-freebsd.h" +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** @name fStateAndGen values + * @{ */ +/** The state bit number. */ +#define RTSEMEVENTMULTIBSD_STATE_BIT 0 +/** The state mask. */ +#define RTSEMEVENTMULTIBSD_STATE_MASK RT_BIT_32(RTSEMEVENTMULTIBSD_STATE_BIT) +/** The generation mask. */ +#define RTSEMEVENTMULTIBSD_GEN_MASK ~RTSEMEVENTMULTIBSD_STATE_MASK +/** The generation shift. */ +#define RTSEMEVENTMULTIBSD_GEN_SHIFT 1 +/** The initial variable value. */ +#define RTSEMEVENTMULTIBSD_STATE_GEN_INIT UINT32_C(0xfffffffc) +/** @} */ + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * FreeBSD multiple release event semaphore. + */ +typedef struct RTSEMEVENTMULTIINTERNAL +{ + /** Magic value (RTSEMEVENTMULTI_MAGIC). */ + uint32_t volatile u32Magic; + /** The object state bit and generation counter. + * The generation counter is incremented every time the object is + * signalled. */ + uint32_t volatile fStateAndGen; + /** Reference counter. */ + uint32_t volatile cRefs; +} RTSEMEVENTMULTIINTERNAL, *PRTSEMEVENTMULTIINTERNAL; + + +RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem) +{ + return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL); +} + + +RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass, + const char *pszNameFmt, ...) +{ + PRTSEMEVENTMULTIINTERNAL pThis; + + AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER); + pThis = (PRTSEMEVENTMULTIINTERNAL)RTMemAlloc(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTSEMEVENTMULTI_MAGIC; + pThis->fStateAndGen = RTSEMEVENTMULTIBSD_STATE_GEN_INIT; + pThis->cRefs = 1; + + *phEventMultiSem = pThis; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +/** + * Retain a reference to the semaphore. + * + * @param pThis The semaphore. + */ +DECLINLINE(void) rtR0SemEventMultiBsdRetain(PRTSEMEVENTMULTIINTERNAL pThis) +{ + uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); + Assert(cRefs && cRefs < 100000); +} + + +/** + * Release a reference, destroy the thing if necessary. + * + * @param pThis The semaphore. + */ +DECLINLINE(void) rtR0SemEventMultiBsdRelease(PRTSEMEVENTMULTIINTERNAL pThis) +{ + if (RT_UNLIKELY(ASMAtomicDecU32(&pThis->cRefs) == 0)) + { + Assert(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC); + RTMemFree(pThis); + } +} + + +RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem; + if (pThis == NIL_RTSEMEVENTMULTI) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + Assert(pThis->cRefs > 0); + + /* + * Invalidate it and signal the object just in case. + */ + ASMAtomicWriteU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC); + ASMAtomicAndU32(&pThis->fStateAndGen, RTSEMEVENTMULTIBSD_GEN_MASK); + rtR0SemBsdBroadcast(pThis); + rtR0SemEventMultiBsdRelease(pThis); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem) +{ + uint32_t fNew; + uint32_t fOld; + + /* + * Validate input. + */ + PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem; + if (!pThis) + return VERR_INVALID_PARAMETER; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + rtR0SemEventMultiBsdRetain(pThis); + + /* + * Signal the event object. The cause of the parnoia here is racing to try + * deal with racing RTSemEventMultiSignal calls (should probably be + * forbidden, but it's relatively easy to handle). + */ + do + { + fNew = fOld = ASMAtomicUoReadU32(&pThis->fStateAndGen); + fNew += 1 << RTSEMEVENTMULTIBSD_GEN_SHIFT; + fNew |= RTSEMEVENTMULTIBSD_STATE_MASK; + } + while (!ASMAtomicCmpXchgU32(&pThis->fStateAndGen, fNew, fOld)); + + rtR0SemBsdBroadcast(pThis); + rtR0SemEventMultiBsdRelease(pThis); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem) +{ + /* + * Validate input. + */ + PRTSEMEVENTMULTIINTERNAL pThis = (PRTSEMEVENTMULTIINTERNAL)hEventMultiSem; + if (!pThis) + return VERR_INVALID_PARAMETER; + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + rtR0SemEventMultiBsdRetain(pThis); + + /* + * Reset it. + */ + ASMAtomicAndU32(&pThis->fStateAndGen, ~RTSEMEVENTMULTIBSD_STATE_MASK); + + rtR0SemEventMultiBsdRelease(pThis); + return VINF_SUCCESS; +} + + +/** + * Worker for RTSemEventMultiWaitEx and RTSemEventMultiWaitExDebug. + * + * @returns VBox status code. + * @param pThis The event semaphore. + * @param fFlags See RTSemEventMultiWaitEx. + * @param uTimeout See RTSemEventMultiWaitEx. + * @param pSrcPos The source code position of the wait. + */ +static int rtR0SemEventMultiBsdWait(PRTSEMEVENTMULTIINTERNAL pThis, uint32_t fFlags, uint64_t uTimeout, + PCRTLOCKVALSRCPOS pSrcPos) +{ + uint32_t fOrgStateAndGen; + int rc; + + /* + * Validate the input. + */ + AssertPtrReturn(pThis, VERR_INVALID_PARAMETER); + AssertMsgReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, ("%p u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_PARAMETER); + AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER); + rtR0SemEventMultiBsdRetain(pThis); + + /* + * Is the event already signalled or do we have to wait? + */ + fOrgStateAndGen = ASMAtomicUoReadU32(&pThis->fStateAndGen); + if (fOrgStateAndGen & RTSEMEVENTMULTIBSD_STATE_MASK) + rc = VINF_SUCCESS; + else + { + /* + * We have to wait. + */ + RTR0SEMBSDSLEEP Wait; + rc = rtR0SemBsdWaitInit(&Wait, fFlags, uTimeout, pThis); + if (RT_SUCCESS(rc)) + { + for (;;) + { + /* The destruction test. */ + if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC)) + rc = VERR_SEM_DESTROYED; + else + { + rtR0SemBsdWaitPrepare(&Wait); + + /* Check the exit conditions. */ + if (RT_UNLIKELY(pThis->u32Magic != RTSEMEVENTMULTI_MAGIC)) + rc = VERR_SEM_DESTROYED; + else if (ASMAtomicUoReadU32(&pThis->fStateAndGen) != fOrgStateAndGen) + rc = VINF_SUCCESS; + else if (rtR0SemBsdWaitHasTimedOut(&Wait)) + rc = VERR_TIMEOUT; + else if (rtR0SemBsdWaitWasInterrupted(&Wait)) + rc = VERR_INTERRUPTED; + else + { + /* Do the wait and then recheck the conditions. */ + rtR0SemBsdWaitDoIt(&Wait); + continue; + } + } + break; + } + + rtR0SemBsdWaitDelete(&Wait); + } + } + + rtR0SemEventMultiBsdRelease(pThis); + return rc; +} + + +RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout) +{ +#ifndef RTSEMEVENT_STRICT + return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, NULL); +#else + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API(); + return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, &SrcPos); +#endif +} +RT_EXPORT_SYMBOL(RTSemEventMultiWaitEx); + + +RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API(); + return rtR0SemEventMultiBsdWait(hEventMultiSem, fFlags, uTimeout, &SrcPos); +} +RT_EXPORT_SYMBOL(RTSemEventMultiWaitExDebug); + + +RTDECL(uint32_t) RTSemEventMultiGetResolution(void) +{ + return rtR0SemBsdWaitGetResolution(); +} +RT_EXPORT_SYMBOL(RTSemEventMultiGetResolution); + diff --git a/src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c new file mode 100644 index 00000000..28517795 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/semfastmutex-r0drv-freebsd.c @@ -0,0 +1,115 @@ +/* $Id: semfastmutex-r0drv-freebsd.c $ */ +/** @file + * IPRT - Fast Mutex Semaphores, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/semaphore.h> +#include <iprt/errcore.h> +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/asm.h> + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Wrapper for the FreeBSD (sleep) mutex. + */ +typedef struct RTSEMFASTMUTEXINTERNAL +{ + /** Magic value (RTSEMFASTMUTEX_MAGIC). */ + uint32_t u32Magic; + /** The FreeBSD shared/exclusive lock mutex. */ + struct sx SxLock; +} RTSEMFASTMUTEXINTERNAL, *PRTSEMFASTMUTEXINTERNAL; + + +RTDECL(int) RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx) +{ + AssertCompile(sizeof(RTSEMFASTMUTEXINTERNAL) > sizeof(void *)); + AssertPtrReturn(phFastMtx, VERR_INVALID_POINTER); + + PRTSEMFASTMUTEXINTERNAL pThis = (PRTSEMFASTMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTSEMFASTMUTEX_MAGIC; + sx_init_flags(&pThis->SxLock, "IPRT Fast Mutex Semaphore", SX_DUPOK); + + *phFastMtx = pThis; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +RTDECL(int) RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx) +{ + PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx; + if (pThis == NIL_RTSEMFASTMUTEX) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + ASMAtomicWriteU32(&pThis->u32Magic, RTSEMFASTMUTEX_MAGIC_DEAD); + sx_destroy(&pThis->SxLock); + RTMemFree(pThis); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx) +{ + PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + sx_xlock(&pThis->SxLock); + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx) +{ + PRTSEMFASTMUTEXINTERNAL pThis = hFastMtx; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMFASTMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + sx_xunlock(&pThis->SxLock); + return VINF_SUCCESS; +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c new file mode 100644 index 00000000..fbb44d4a --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/semmutex-r0drv-freebsd.c @@ -0,0 +1,219 @@ +/* $Id: semmutex-r0drv-freebsd.c $ */ +/** @file + * IPRT - Mutex Semaphores, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (C) 2010-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define RTSEMMUTEX_WITHOUT_REMAPPING +#include "the-freebsd-kernel.h" +#include "internal/iprt.h" +#include <iprt/semaphore.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/thread.h> +#include <iprt/time.h> + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Wrapper for the FreeBSD (sleep) mutex. + */ +typedef struct RTSEMMUTEXINTERNAL +{ + /** Magic value (RTSEMMUTEX_MAGIC). */ + uint32_t u32Magic; + /** The FreeBSD shared/exclusive lock mutex. */ + struct sx SxLock; +} RTSEMMUTEXINTERNAL, *PRTSEMMUTEXINTERNAL; + + +RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem) +{ + AssertCompile(sizeof(RTSEMMUTEXINTERNAL) > sizeof(void *)); + AssertPtrReturn(phMutexSem, VERR_INVALID_POINTER); + + PRTSEMMUTEXINTERNAL pThis = (PRTSEMMUTEXINTERNAL)RTMemAllocZ(sizeof(*pThis)); + if (pThis) + { + pThis->u32Magic = RTSEMMUTEX_MAGIC; + sx_init_flags(&pThis->SxLock, "IPRT Mutex Semaphore", SX_RECURSE); + + *phMutexSem = pThis; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + +RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem) +{ + PRTSEMMUTEXINTERNAL pThis = hMutexSem; + if (pThis == NIL_RTSEMMUTEX) + return VINF_SUCCESS; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE); + + sx_destroy(&pThis->SxLock); + RTMemFree(pThis); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemMutexRequest(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) +{ + PRTSEMMUTEXINTERNAL pThis = hMutexSem; + int rc; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + if (cMillies == RT_INDEFINITE_WAIT) + { + sx_xlock(&pThis->SxLock); + rc = VINF_SUCCESS; + } + else if (!cMillies) + { + if (sx_try_xlock(&pThis->SxLock)) + rc = VINF_SUCCESS; + else + rc = VERR_TIMEOUT; + } + /* + * GROSS HACK: poll implementation of timeout. + */ + /** @todo Implement timeouts in RTSemMutexRequest. */ + else if (sx_try_xlock(&pThis->SxLock)) + rc = VINF_SUCCESS; + else + { + uint64_t StartTS = RTTimeSystemMilliTS(); + rc = VERR_TIMEOUT; + do + { + RTThreadSleep(1); + if (sx_try_xlock(&pThis->SxLock)) + { + rc = VINF_SUCCESS; + break; + } + } while (RTTimeSystemMilliTS() - StartTS < cMillies); + } + + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemMutexRequestDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + return RTSemMutexRequest(hMutexSem, cMillies); +} + + +RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies) +{ + PRTSEMMUTEXINTERNAL pThis = hMutexSem; + int rc; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + if (cMillies == RT_INDEFINITE_WAIT) + { + if (!sx_xlock_sig(&pThis->SxLock)) + rc = VINF_SUCCESS; + else + rc = VERR_INTERRUPTED; + } + else if (!cMillies) + { + if (sx_try_xlock(&pThis->SxLock)) + rc = VINF_SUCCESS; + else + rc = VERR_TIMEOUT; + } + /* + * GROSS HACK: poll implementation of timeout. + */ + /** @todo Implement timeouts and interrupt checks in + * RTSemMutexRequestNoResume. */ + else if (sx_try_xlock(&pThis->SxLock)) + rc = VINF_SUCCESS; + else + { + uint64_t StartTS = RTTimeSystemMilliTS(); + rc = VERR_TIMEOUT; + do + { + RTThreadSleep(1); + if (sx_try_xlock(&pThis->SxLock)) + { + rc = VINF_SUCCESS; + break; + } + } while (RTTimeSystemMilliTS() - StartTS < cMillies); + } + + return VINF_SUCCESS; +} + + +RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + return RTSemMutexRequestNoResume(hMutexSem, cMillies); +} + + +RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem) +{ + PRTSEMMUTEXINTERNAL pThis = hMutexSem; + AssertPtrReturn(pThis, VERR_INVALID_HANDLE); + AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), VERR_INVALID_HANDLE); + + sx_xunlock(&pThis->SxLock); + return VINF_SUCCESS; +} + + + +RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem) +{ + PRTSEMMUTEXINTERNAL pThis = hMutexSem; + AssertPtrReturn(pThis, false); + AssertMsgReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, ("%p: u32Magic=%RX32\n", pThis, pThis->u32Magic), false); + + return sx_xlocked(&pThis->SxLock); +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h b/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h new file mode 100644 index 00000000..dc1c3307 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/sleepqueue-r0drv-freebsd.h @@ -0,0 +1,334 @@ +/* $Id: sleepqueue-r0drv-freebsd.h $ */ +/** @file + * IPRT - FreeBSD Ring-0 Driver Helpers for Abstracting Sleep Queues, + */ + +/* + * Copyright (C) 2006-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h +#define IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "the-freebsd-kernel.h" + +#include <iprt/asm-math.h> +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/time.h> + +/** + * Kernel mode FreeBSD wait state structure. + */ +typedef struct RTR0SEMBSDSLEEP +{ + /** The absolute timeout given as nano seconds since the start of the + * monotonic clock. */ + uint64_t uNsAbsTimeout; + /** The timeout in ticks. Updated after waiting. */ + int iTimeout; + /** Set if it's an indefinite wait. */ + bool fIndefinite; + /** Set if we've already timed out. + * Set by rtR0SemBsdWaitDoIt and read by rtR0SemBsdWaitHasTimedOut. */ + bool fTimedOut; + /** Flag whether the wait was interrupted. */ + bool fInterrupted; + /** flag whether the wait is interruptible or not. */ + bool fInterruptible; + /** Opaque wait channel id. */ + void *pvWaitChan; +} RTR0SEMBSDSLEEP; +/** Pointer to a FreeBSD wait state. */ +typedef RTR0SEMBSDSLEEP *PRTR0SEMBSDSLEEP; + + +/** + * Updates the timeout of the FreeBSD wait. + * + * @returns RTSEMWAIT_FLAGS_INDEFINITE if the timeout value is too big. + * 0 otherwise + * @param pWait The wait structure. + * @param uTimeout The relative timeout in nanoseconds. + */ +DECLINLINE(uint32_t) rtR0SemBsdWaitUpdateTimeout(PRTR0SEMBSDSLEEP pWait, uint64_t uTimeout) +{ +#if 0 + struct timeval tv; + + tv.tv_sec = uTimeout / UINT64_C(1000000000); + tv.tv_usec = (uTimeout % UINT64_C(1000000000)) / UINT64_C(1000); + + pWait->iTimeout = tvtohz(&tv); +#else + uint64_t cTicks = ASMMultU64ByU32DivByU32(uTimeout, hz, UINT32_C(1000000000)); + if (cTicks >= INT_MAX) + return RTSEMWAIT_FLAGS_INDEFINITE; + else + pWait->iTimeout = (int)cTicks; +#endif + + return 0; +} + +/** + * Initializes a wait. + * + * The caller MUST check the wait condition BEFORE calling this function or the + * timeout logic will be flawed. + * + * @returns VINF_SUCCESS or VERR_TIMEOUT. + * @param pWait The wait structure. + * @param fFlags The wait flags. + * @param uTimeout The timeout. + * @param pvWaitChan The opaque wait channel. + */ +DECLINLINE(int) rtR0SemBsdWaitInit(PRTR0SEMBSDSLEEP pWait, uint32_t fFlags, uint64_t uTimeout, + void *pvWaitChan) +{ + pWait->iTimeout = 0; + pWait->uNsAbsTimeout = 0; /* shut up gcc */ + + /* + * Process the flags and timeout. + */ + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)) + { +/** @todo optimize: millisecs -> nanosecs -> millisec -> jiffies */ + if (fFlags & RTSEMWAIT_FLAGS_MILLISECS) + uTimeout = uTimeout < UINT64_MAX / UINT32_C(1000000) * UINT32_C(1000000) + ? uTimeout * UINT32_C(1000000) + : UINT64_MAX; + if (uTimeout == UINT64_MAX) + fFlags |= RTSEMWAIT_FLAGS_INDEFINITE; + else + { + uint64_t u64Now; + if (fFlags & RTSEMWAIT_FLAGS_RELATIVE) + { + if (uTimeout == 0) + return VERR_TIMEOUT; + + u64Now = RTTimeSystemNanoTS(); + if (u64Now + uTimeout < u64Now) /* overflow */ + fFlags |= RTSEMWAIT_FLAGS_INDEFINITE; + else + pWait->uNsAbsTimeout = u64Now + uTimeout; + } + else + { + u64Now = RTTimeSystemNanoTS(); + if (u64Now >= uTimeout) + return VERR_TIMEOUT; + + pWait->uNsAbsTimeout = uTimeout; + uTimeout -= u64Now; /* Get a relative value. */ + } + } + } + + if (!(fFlags & RTSEMWAIT_FLAGS_INDEFINITE)) + { + pWait->fIndefinite = false; + fFlags |= rtR0SemBsdWaitUpdateTimeout(pWait, uTimeout); + } + + if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE) + { + pWait->fIndefinite = true; + pWait->iTimeout = INT_MAX; + pWait->uNsAbsTimeout = UINT64_MAX; + } + + pWait->fTimedOut = false; + + /* + * Initialize the wait queue related bits. + */ + pWait->fInterruptible = fFlags & RTSEMWAIT_FLAGS_INTERRUPTIBLE + ? true : false; + pWait->pvWaitChan = pvWaitChan; + pWait->fInterrupted = false; + + return VINF_SUCCESS; +} + +/** + * Prepares the next wait. + * + * This must be called before rtR0SemBsdWaitDoIt, and the caller should check + * the exit conditions inbetween the two calls. + * + * @param pWait The wait structure. + */ +DECLINLINE(void) rtR0SemBsdWaitPrepare(PRTR0SEMBSDSLEEP pWait) +{ + /* Lock the queues. */ + sleepq_lock(pWait->pvWaitChan); +} + +/** + * Do the actual wait. + * + * @param pWait The wait structure. + */ +DECLINLINE(void) rtR0SemBsdWaitDoIt(PRTR0SEMBSDSLEEP pWait) +{ + int rcBsd; + int fSleepqFlags = SLEEPQ_CONDVAR; + + if (pWait->fInterruptible) + fSleepqFlags |= SLEEPQ_INTERRUPTIBLE; + + sleepq_add(pWait->pvWaitChan, NULL, "VBoxIS", fSleepqFlags, 0); + + if (!pWait->fIndefinite) + { + sleepq_set_timeout(pWait->pvWaitChan, pWait->iTimeout); + + if (pWait->fInterruptible) + rcBsd = SLEEPQ_TIMEDWAIT_SIG(pWait->pvWaitChan); + else + rcBsd = SLEEPQ_TIMEDWAIT(pWait->pvWaitChan); + } + else + { + if (pWait->fInterruptible) + rcBsd = SLEEPQ_WAIT_SIG(pWait->pvWaitChan); + else + { + rcBsd = 0; + SLEEPQ_WAIT(pWait->pvWaitChan); + } + } + + switch (rcBsd) + { + case 0: + break; + case ERESTART: + { + if (!pWait->fIndefinite) + { + /* Recalc timeout. */ + uint64_t u64Now = RTTimeSystemNanoTS(); + if (u64Now >= pWait->uNsAbsTimeout) + pWait->fTimedOut = true; + else + { + u64Now = pWait->uNsAbsTimeout - u64Now; + rtR0SemBsdWaitUpdateTimeout(pWait, u64Now); + } + } + break; + } + case EWOULDBLOCK: + pWait->fTimedOut = true; + break; + case EINTR: + Assert(pWait->fInterruptible); + pWait->fInterrupted = true; + break; + default: + AssertMsgFailed(("sleepq_* -> %d\n", rcBsd)); + break; + } +} + + +/** + * Checks if a FreeBSD wait was interrupted. + * + * @returns true / false + * @param pWait The wait structure. + * @remarks This shall be called before the first rtR0SemBsdWaitDoIt(). + */ +DECLINLINE(bool) rtR0SemBsdWaitWasInterrupted(PRTR0SEMBSDSLEEP pWait) +{ + return pWait->fInterrupted; +} + + +/** + * Checks if a FreeBSD wait has timed out. + * + * @returns true / false + * @param pWait The wait structure. + */ +DECLINLINE(bool) rtR0SemBsdWaitHasTimedOut(PRTR0SEMBSDSLEEP pWait) +{ + return pWait->fTimedOut; +} + + +/** + * Deletes a FreeBSD wait. + * + * @param pWait The wait structure. + */ +DECLINLINE(void) rtR0SemBsdWaitDelete(PRTR0SEMBSDSLEEP pWait) +{ + sleepq_release(pWait->pvWaitChan); +} + + +/** + * Signals the wait channel. + * + * @param pvWaitChan The opaque wait channel handle. + */ +DECLINLINE(void) rtR0SemBsdSignal(void *pvWaitChan) +{ + sleepq_lock(pvWaitChan); + int fWakeupSwapProc = sleepq_signal(pvWaitChan, SLEEPQ_CONDVAR, 0, 0); + sleepq_release(pvWaitChan); + if (fWakeupSwapProc) + kick_proc0(); +} + +/** + * Wakes up all waiters on the wait channel. + * + * @param pvWaitChan The opaque wait channel handle. + */ +DECLINLINE(void) rtR0SemBsdBroadcast(void *pvWaitChan) +{ + sleepq_lock(pvWaitChan); + sleepq_broadcast(pvWaitChan, SLEEPQ_CONDVAR, 0, 0); +#if __FreeBSD_version >= 800000 /* Broadcast releases the sleep queue lock on FreeBSD 7.x */ + sleepq_release(pvWaitChan); +#endif +} + +/** + * Gets the max resolution of the timeout machinery. + * + * @returns Resolution specified in nanoseconds. + */ +DECLINLINE(uint32_t) rtR0SemBsdWaitGetResolution(void) +{ + return 1000000000 / hz; /* ns */ +} + +#endif /* !IPRT_INCLUDED_SRC_r0drv_freebsd_sleepqueue_r0drv_freebsd_h */ + diff --git a/src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c new file mode 100644 index 00000000..d493290a --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/spinlock-r0drv-freebsd.c @@ -0,0 +1,210 @@ +/* $Id: spinlock-r0drv-freebsd.c $ */ +/** @file + * IPRT - Spinlocks, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" +#include "internal/iprt.h" + +#include <iprt/spinlock.h> +#include <iprt/errcore.h> +#include <iprt/alloc.h> +#include <iprt/assert.h> +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/thread.h> +#include <iprt/mp.h> + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Wrapper for the struct mtx type. + */ +typedef struct RTSPINLOCKINTERNAL +{ + /** Spinlock magic value (RTSPINLOCK_MAGIC). */ + uint32_t volatile u32Magic; + /** The spinlock. */ + uint32_t volatile fLocked; + /** Saved interrupt flag. */ + uint32_t volatile fIntSaved; + /** The spinlock creation flags. */ + uint32_t fFlags; +#ifdef RT_MORE_STRICT + /** The idAssertCpu variable before acquring the lock for asserting after + * releasing the spinlock. */ + RTCPUID volatile idAssertCpu; + /** The CPU that owns the lock. */ + RTCPUID volatile idCpuOwner; +#endif +} RTSPINLOCKINTERNAL, *PRTSPINLOCKINTERNAL; + + +RTDECL(int) RTSpinlockCreate(PRTSPINLOCK pSpinlock, uint32_t fFlags, const char *pszName) +{ + RT_ASSERT_PREEMPTIBLE(); + AssertReturn(fFlags == RTSPINLOCK_FLAGS_INTERRUPT_SAFE || fFlags == RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, VERR_INVALID_PARAMETER); + + /* + * Allocate. + */ + AssertCompile(sizeof(RTSPINLOCKINTERNAL) > sizeof(void *)); + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)RTMemAllocZ(sizeof(*pThis)); + if (!pThis) + return VERR_NO_MEMORY; + + /* + * Initialize & return. + */ + pThis->u32Magic = RTSPINLOCK_MAGIC; + pThis->fLocked = 0; + pThis->fFlags = fFlags; + pThis->fIntSaved = 0; + + *pSpinlock = pThis; + return VINF_SUCCESS; +} + + +RTDECL(int) RTSpinlockDestroy(RTSPINLOCK Spinlock) +{ + /* + * Validate input. + */ + RT_ASSERT_INTS_ON(); + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock; + if (!pThis) + return VERR_INVALID_PARAMETER; + AssertMsgReturn(pThis->u32Magic == RTSPINLOCK_MAGIC, + ("Invalid spinlock %p magic=%#x\n", pThis, pThis->u32Magic), + VERR_INVALID_PARAMETER); + + /* + * Make the lock invalid and release the memory. + */ + ASMAtomicIncU32(&pThis->u32Magic); + RTMemFree(pThis); + return VINF_SUCCESS; +} + + +RTDECL(void) RTSpinlockAcquire(RTSPINLOCK Spinlock) +{ + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock; + RT_ASSERT_PREEMPT_CPUID_VAR(); + AssertPtr(pThis); + Assert(pThis->u32Magic == RTSPINLOCK_MAGIC); + + if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE) + { + for (;;) + { + uint32_t fIntSaved = ASMIntDisableFlags(); + critical_enter(); + + int c = 50; + for (;;) + { + if (ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0)) + { + RT_ASSERT_PREEMPT_CPUID_SPIN_ACQUIRED(pThis); + pThis->fIntSaved = fIntSaved; + return; + } + if (--c <= 0) + break; + cpu_spinwait(); + } + + /* Enable interrupts while we sleep. */ + ASMSetFlags(fIntSaved); + critical_exit(); + DELAY(1); + } + } + else + { + for (;;) + { + critical_enter(); + + int c = 50; + for (;;) + { + if (ASMAtomicCmpXchgU32(&pThis->fLocked, 1, 0)) + { + RT_ASSERT_PREEMPT_CPUID_SPIN_ACQUIRED(pThis); + return; + } + if (--c <= 0) + break; + cpu_spinwait(); + } + + critical_exit(); + DELAY(1); + } + } +} + + +RTDECL(void) RTSpinlockRelease(RTSPINLOCK Spinlock) +{ + PRTSPINLOCKINTERNAL pThis = (PRTSPINLOCKINTERNAL)Spinlock; + RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE_VARS(); + + AssertPtr(pThis); + Assert(pThis->u32Magic == RTSPINLOCK_MAGIC); + RT_ASSERT_PREEMPT_CPUID_SPIN_RELEASE(pThis); + + if (pThis->fFlags & RTSPINLOCK_FLAGS_INTERRUPT_SAFE) + { + uint32_t fIntSaved = pThis->fIntSaved; + pThis->fIntSaved = 0; + if (ASMAtomicCmpXchgU32(&pThis->fLocked, 0, 1)) + ASMSetFlags(fIntSaved); + else + AssertMsgFailed(("Spinlock %p was not locked!\n", pThis)); + } + else + { + if (!ASMAtomicCmpXchgU32(&pThis->fLocked, 0, 1)) + AssertMsgFailed(("Spinlock %p was not locked!\n", pThis)); + } + + critical_exit(); +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h b/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h new file mode 100644 index 00000000..eb4019a2 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/the-freebsd-kernel.h @@ -0,0 +1,122 @@ +/* $Id: the-freebsd-kernel.h $ */ +/** @file + * IPRT - Ring-0 Driver, The FreeBSD Kernel Headers. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef IPRT_INCLUDED_SRC_r0drv_freebsd_the_freebsd_kernel_h +#define IPRT_INCLUDED_SRC_r0drv_freebsd_the_freebsd_kernel_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> + +/* Deal with conflicts first. */ +#include <sys/param.h> +#undef PVM +#include <sys/bus.h> +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/uio.h> +#include <sys/libkern.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/pcpu.h> +#include <sys/proc.h> +#include <sys/limits.h> +#include <sys/unistd.h> +#include <sys/kthread.h> +#include <sys/lock.h> +#if __FreeBSD_version >= 1000030 +#include <sys/rwlock.h> +#endif +#include <sys/mutex.h> +#include <sys/sched.h> +#include <sys/callout.h> +#include <sys/cpu.h> +#include <sys/smp.h> +#include <sys/sleepqueue.h> +#include <sys/sx.h> +#include <vm/vm.h> +#include <vm/pmap.h> /* for vtophys */ +#include <vm/vm_map.h> +#include <vm/vm_object.h> +#include <vm/vm_kern.h> +#include <vm/vm_param.h> /* KERN_SUCCESS ++ */ +#include <vm/vm_page.h> +#include <vm/vm_phys.h> /* vm_phys_alloc_* */ +#include <vm/vm_extern.h> /* kmem_alloc_attr */ +#include <vm/vm_pageout.h> /* vm_contig_grow_cache */ +#include <sys/vmmeter.h> /* cnt */ +#include <sys/resourcevar.h> +#include <machine/cpu.h> + +/** + * Wrappers around the sleepq_ KPI. + */ +#if __FreeBSD_version >= 800026 +# define SLEEPQ_TIMEDWAIT(EventInt) sleepq_timedwait(EventInt, 0) +# define SLEEPQ_TIMEDWAIT_SIG(EventInt) sleepq_timedwait_sig(EventInt, 0) +# define SLEEPQ_WAIT(EventInt) sleepq_wait(EventInt, 0) +# define SLEEPQ_WAIT_SIG(EventInt) sleepq_wait_sig(EventInt, 0) +#else +# define SLEEPQ_TIMEDWAIT(EventInt) sleepq_timedwait(EventInt) +# define SLEEPQ_TIMEDWAIT_SIG(EventInt) sleepq_timedwait_sig(EventInt) +# define SLEEPQ_WAIT(EventInt) sleepq_wait(EventInt) +# define SLEEPQ_WAIT_SIG(EventInt) sleepq_wait_sig(EventInt) +#endif + +/** + * Our pmap_enter version + */ +#if __FreeBSD_version >= 701105 +# define MY_PMAP_ENTER(pPhysMap, AddrR3, pPage, fProt, fWired) \ + pmap_enter(pPhysMap, AddrR3, VM_PROT_NONE, pPage, fProt, fWired) +#else +# define MY_PMAP_ENTER(pPhysMap, AddrR3, pPage, fProt, fWired) \ + pmap_enter(pPhysMap, AddrR3, pPage, fProt, fWired) +#endif + +/** + * Check whether we can use kmem_alloc_attr for low allocs. + */ +#if (__FreeBSD_version >= 900011) \ + || (__FreeBSD_version < 900000 && __FreeBSD_version >= 800505) \ + || (__FreeBSD_version < 800000 && __FreeBSD_version >= 703101) +# define USE_KMEM_ALLOC_ATTR +#endif + +/** + * Check whether we can use kmem_alloc_prot. + */ +#if 0 /** @todo Not available yet. */ +# define USE_KMEM_ALLOC_PROT +#endif + +#endif /* !IPRT_INCLUDED_SRC_r0drv_freebsd_the_freebsd_kernel_h */ diff --git a/src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c new file mode 100644 index 00000000..3fac9d4b --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/thread-r0drv-freebsd.c @@ -0,0 +1,186 @@ +/* $Id: thread-r0drv-freebsd.c $ */ +/** @file + * IPRT - Threads (Part 1), Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (C) 2007-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" +#include "internal/iprt.h" +#include <iprt/thread.h> + +#include <iprt/asm.h> +#include <iprt/asm-amd64-x86.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/mp.h> +#include "internal/thread.h" + + +RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void) +{ + return (RTNATIVETHREAD)curthread; +} + + +static int rtR0ThreadFbsdSleepCommon(RTMSINTERVAL cMillies) +{ + int rc; + int cTicks; + + /* + * 0 ms sleep -> yield. + */ + if (!cMillies) + { + RTThreadYield(); + return VINF_SUCCESS; + } + + /* + * Translate milliseconds into ticks and go to sleep. + */ + if (cMillies != RT_INDEFINITE_WAIT) + { + if (hz == 1000) + cTicks = cMillies; + else if (hz == 100) + cTicks = cMillies / 10; + else + { + int64_t cTicks64 = ((uint64_t)cMillies * hz) / 1000; + cTicks = (int)cTicks64; + if (cTicks != cTicks64) + cTicks = INT_MAX; + } + } + else + cTicks = 0; /* requires giant lock! */ + + rc = tsleep((void *)RTThreadSleep, + PZERO | PCATCH, + "iprtsl", /* max 6 chars */ + cTicks); + switch (rc) + { + case 0: + return VINF_SUCCESS; + case EWOULDBLOCK: + return VERR_TIMEOUT; + case EINTR: + case ERESTART: + return VERR_INTERRUPTED; + default: + AssertMsgFailed(("%d\n", rc)); + return VERR_NO_TRANSLATION; + } +} + + +RTDECL(int) RTThreadSleep(RTMSINTERVAL cMillies) +{ + return rtR0ThreadFbsdSleepCommon(cMillies); +} + + +RTDECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies) +{ + return rtR0ThreadFbsdSleepCommon(cMillies); +} + + +RTDECL(bool) RTThreadYield(void) +{ +#if __FreeBSD_version >= 900032 + kern_yield(curthread->td_user_pri); +#else + uio_yield(); +#endif + return false; /** @todo figure this one ... */ +} + + +RTDECL(bool) RTThreadPreemptIsEnabled(RTTHREAD hThread) +{ + Assert(hThread == NIL_RTTHREAD); + + return curthread->td_critnest == 0 + && ASMIntAreEnabled(); /** @todo is there a native freebsd function/macro for this? */ +} + + +RTDECL(bool) RTThreadPreemptIsPending(RTTHREAD hThread) +{ + Assert(hThread == NIL_RTTHREAD); + + return curthread->td_owepreempt == 1; +} + + +RTDECL(bool) RTThreadPreemptIsPendingTrusty(void) +{ + /* yes, RTThreadPreemptIsPending is reliable. */ + return true; +} + + +RTDECL(bool) RTThreadPreemptIsPossible(void) +{ + /* yes, kernel preemption is possible. */ + return true; +} + + +RTDECL(void) RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState) +{ + AssertPtr(pState); + Assert(pState->u32Reserved == 0); + pState->u32Reserved = 42; + + critical_enter(); + RT_ASSERT_PREEMPT_CPUID_DISABLE(pState); +} + + +RTDECL(void) RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState) +{ + AssertPtr(pState); + Assert(pState->u32Reserved == 42); + pState->u32Reserved = 0; + + RT_ASSERT_PREEMPT_CPUID_RESTORE(pState); + critical_exit(); +} + + +RTDECL(bool) RTThreadIsInInterrupt(RTTHREAD hThread) +{ + Assert(hThread == NIL_RTTHREAD); NOREF(hThread); + /** @todo FreeBSD: Implement RTThreadIsInInterrupt. Required for guest + * additions! */ + return !ASMIntAreEnabled(); +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c new file mode 100644 index 00000000..0092232e --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/thread2-r0drv-freebsd.c @@ -0,0 +1,155 @@ +/* $Id: thread2-r0drv-freebsd.c $ */ +/** @file + * IPRT - Threads (Part 2), Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/thread.h> +#include <iprt/errcore.h> +#include <iprt/assert.h> + +#include "internal/thread.h" + + +DECLHIDDEN(int) rtThreadNativeInit(void) +{ + return VINF_SUCCESS; +} + + +RTDECL(RTTHREAD) RTThreadSelf(void) +{ + return rtThreadGetByNative(RTThreadNativeSelf()); +} + + +DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType) +{ + int iPriority; + + switch (enmType) + { + case RTTHREADTYPE_INFREQUENT_POLLER: iPriority = PZERO + 8; break; + case RTTHREADTYPE_EMULATION: iPriority = PZERO + 4; break; + case RTTHREADTYPE_DEFAULT: iPriority = PZERO; break; + case RTTHREADTYPE_MSG_PUMP: iPriority = PZERO - 4; break; + case RTTHREADTYPE_IO: iPriority = PRIBIO; break; + case RTTHREADTYPE_TIMER: iPriority = PRI_MIN_KERN; break; + default: + AssertMsgFailed(("enmType=%d\n", enmType)); + return VERR_INVALID_PARAMETER; + } + +#if __FreeBSD_version < 700000 + /* Do like they're doing in subr_ntoskrnl.c... */ + mtx_lock_spin(&sched_lock); +#else + thread_lock(curthread); +#endif + sched_prio(curthread, iPriority); +#if __FreeBSD_version < 600000 + curthread->td_base_pri = iPriority; +#endif +#if __FreeBSD_version < 700000 + mtx_unlock_spin(&sched_lock); +#else + thread_unlock(curthread); +#endif + + return VINF_SUCCESS; +} + + +DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread) +{ + NOREF(pThread); + /* There is nothing special that needs doing here, but the + user really better know what he's cooking. */ + return VINF_SUCCESS; +} + + +DECLHIDDEN(void) rtThreadNativeWaitKludge(PRTTHREADINT pThread) +{ + /** @todo fix RTThreadWait/RTR0Term race on freebsd. */ + RTThreadSleep(1); +} + + +DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread) +{ + NOREF(pThread); +} + + +/** + * Native thread main function. + * + * @param pvThreadInt The thread structure. + */ +static void rtThreadNativeMain(void *pvThreadInt) +{ + const struct thread *Self = curthread; + PRTTHREADINT pThreadInt = (PRTTHREADINT)pvThreadInt; + int rc; + + rc = rtThreadMain(pThreadInt, (RTNATIVETHREAD)Self, &pThreadInt->szName[0]); + +#if __FreeBSD_version >= 800002 + kproc_exit(rc); +#else + kthread_exit(rc); +#endif +} + + +DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThreadInt, PRTNATIVETHREAD pNativeThread) +{ + int rc; + struct proc *pProc; + +#if __FreeBSD_version >= 800002 + rc = kproc_create(rtThreadNativeMain, pThreadInt, &pProc, RFHIGHPID, 0, "%s", pThreadInt->szName); +#else + rc = kthread_create(rtThreadNativeMain, pThreadInt, &pProc, RFHIGHPID, 0, "%s", pThreadInt->szName); +#endif + if (!rc) + { + *pNativeThread = (RTNATIVETHREAD)FIRST_THREAD_IN_PROC(pProc); + rc = VINF_SUCCESS; + } + else + rc = RTErrConvertFromErrno(rc); + return rc; +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c new file mode 100644 index 00000000..20720440 --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/time-r0drv-freebsd.c @@ -0,0 +1,74 @@ +/* $Id: time-r0drv-freebsd.c $ */ +/** @file + * IPRT - Time, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" +#define RTTIME_INCL_TIMESPEC + +#include <iprt/time.h> + + +RTDECL(uint64_t) RTTimeNanoTS(void) +{ + struct timespec tsp; + nanouptime(&tsp); + return tsp.tv_sec * RT_NS_1SEC_64 + + tsp.tv_nsec; +} + + +RTDECL(uint64_t) RTTimeMilliTS(void) +{ + return RTTimeNanoTS() / RT_NS_1MS; +} + + +RTDECL(uint64_t) RTTimeSystemNanoTS(void) +{ + return RTTimeNanoTS(); +} + + +RTDECL(uint64_t) RTTimeSystemMilliTS(void) +{ + return RTTimeMilliTS(); +} + + +RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime) +{ + struct timespec tsp; + nanotime(&tsp); + return RTTimeSpecSetTimespec(pTime, &tsp); +} + diff --git a/src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c b/src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c new file mode 100644 index 00000000..0dbca47a --- /dev/null +++ b/src/VBox/Runtime/r0drv/freebsd/timer-r0drv-freebsd.c @@ -0,0 +1,286 @@ +/* $Id: timer-r0drv-freebsd.c $ */ +/** @file + * IPRT - Memory Allocation, Ring-0 Driver, FreeBSD. + */ + +/* + * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-freebsd-kernel.h" + +#include <iprt/timer.h> +#include <iprt/time.h> +#include <iprt/spinlock.h> +#include <iprt/err.h> +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/alloc.h> + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * The internal representation of an FreeBSD timer handle. + */ +typedef struct RTTIMER +{ + /** Magic. + * This is RTTIMER_MAGIC, but changes to something else before the timer + * is destroyed to indicate clearly that thread should exit. */ + uint32_t volatile u32Magic; + /** Flag indicating that the timer is suspended. */ + uint8_t volatile fSuspended; + /** Whether the timer must run on a specific CPU or not. */ + uint8_t fSpecificCpu; + /** The CPU it must run on if fSpecificCpu is set. */ + uint32_t iCpu; + /** The FreeBSD callout structure. */ + struct callout Callout; + /** Callback. */ + PFNRTTIMER pfnTimer; + /** User argument. */ + void *pvUser; + /** The timer interval. 0 if one-shot. */ + uint64_t u64NanoInterval; + /** The start of the current run. + * This is used to calculate when the timer ought to fire the next time. */ + uint64_t volatile u64StartTS; + /** The start of the current run. + * This is used to calculate when the timer ought to fire the next time. */ + uint64_t volatile u64NextTS; + /** The current tick number (since u64StartTS). */ + uint64_t volatile iTick; +} RTTIMER; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static void rtTimerFreeBSDCallback(void *pvTimer); + + + +RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser) +{ + *ppTimer = NULL; + + /* + * Validate flags. + */ + if (!RTTIMER_FLAGS_ARE_VALID(fFlags)) + return VERR_INVALID_PARAMETER; + if ( (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC) + && (fFlags & RTTIMER_FLAGS_CPU_ALL) != RTTIMER_FLAGS_CPU_ALL + && (fFlags & RTTIMER_FLAGS_CPU_MASK) > mp_maxid) + return VERR_CPU_NOT_FOUND; + + /* + * Allocate and initialize the timer handle. + */ + PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer)); + if (!pTimer) + return VERR_NO_MEMORY; + + pTimer->u32Magic = RTTIMER_MAGIC; + pTimer->fSuspended = true; + pTimer->fSpecificCpu = !!(fFlags & RTTIMER_FLAGS_CPU_SPECIFIC); + pTimer->iCpu = fFlags & RTTIMER_FLAGS_CPU_MASK; + pTimer->pfnTimer = pfnTimer; + pTimer->pvUser = pvUser; + pTimer->u64NanoInterval = u64NanoInterval; + pTimer->u64StartTS = 0; + callout_init(&pTimer->Callout, CALLOUT_MPSAFE); + + *ppTimer = pTimer; + return VINF_SUCCESS; +} + + +/** + * Validates the timer handle. + * + * @returns true if valid, false if invalid. + * @param pTimer The handle. + */ +DECLINLINE(bool) rtTimerIsValid(PRTTIMER pTimer) +{ + AssertReturn(VALID_PTR(pTimer), false); + AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, false); + return true; +} + + +RTDECL(int) RTTimerDestroy(PRTTIMER pTimer) +{ + /* It's ok to pass NULL pointer. */ + if (pTimer == /*NIL_RTTIMER*/ NULL) + return VINF_SUCCESS; + if (!rtTimerIsValid(pTimer)) + return VERR_INVALID_HANDLE; + + /* + * Free the associated resources. + */ + pTimer->u32Magic++; + callout_stop(&pTimer->Callout); + RTMemFree(pTimer); + return VINF_SUCCESS; +} + + +RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First) +{ + struct timeval tv; + + if (!rtTimerIsValid(pTimer)) + return VERR_INVALID_HANDLE; + if (!pTimer->fSuspended) + return VERR_TIMER_ACTIVE; + if ( pTimer->fSpecificCpu + && !RTMpIsCpuOnline(RTMpCpuIdFromSetIndex(pTimer->iCpu))) + return VERR_CPU_OFFLINE; + + /* + * Calc when it should start firing. + */ + u64First += RTTimeNanoTS(); + + pTimer->fSuspended = false; + pTimer->iTick = 0; + pTimer->u64StartTS = u64First; + pTimer->u64NextTS = u64First; + + tv.tv_sec = u64First / 1000000000; + tv.tv_usec = (u64First % 1000000000) / 1000; + callout_reset(&pTimer->Callout, tvtohz(&tv), rtTimerFreeBSDCallback, pTimer); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTTimerStop(PRTTIMER pTimer) +{ + if (!rtTimerIsValid(pTimer)) + return VERR_INVALID_HANDLE; + if (pTimer->fSuspended) + return VERR_TIMER_SUSPENDED; + + /* + * Suspend the timer. + */ + pTimer->fSuspended = true; + callout_stop(&pTimer->Callout); + + return VINF_SUCCESS; +} + + +RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval) +{ + if (!rtTimerIsValid(pTimer)) + return VERR_INVALID_HANDLE; + return VERR_NOT_SUPPORTED; +} + + +/** + * smp_rendezvous action callback. + * + * This will perform the timer callback if we're on the right CPU. + * + * @param pvTimer The timer. + */ +static void rtTimerFreeBSDIpiAction(void *pvTimer) +{ + PRTTIMER pTimer = (PRTTIMER)pvTimer; + if ( pTimer->iCpu == RTTIMER_FLAGS_CPU_MASK + || (u_int)pTimer->iCpu == curcpu) + pTimer->pfnTimer(pTimer, pTimer->pvUser, pTimer->iTick); +} + + +static void rtTimerFreeBSDCallback(void *pvTimer) +{ + PRTTIMER pTimer = (PRTTIMER)pvTimer; + + /* calculate and set the next timeout */ + pTimer->iTick++; + if (!pTimer->u64NanoInterval) + { + pTimer->fSuspended = true; + callout_stop(&pTimer->Callout); + } + else + { + struct timeval tv; + const uint64_t u64NanoTS = RTTimeNanoTS(); + pTimer->u64NextTS = pTimer->u64StartTS + pTimer->iTick * pTimer->u64NanoInterval; + if (pTimer->u64NextTS < u64NanoTS) + pTimer->u64NextTS = u64NanoTS + RTTimerGetSystemGranularity() / 2; + + tv.tv_sec = pTimer->u64NextTS / 1000000000; + tv.tv_usec = (pTimer->u64NextTS % 1000000000) / 1000; + callout_reset(&pTimer->Callout, tvtohz(&tv), rtTimerFreeBSDCallback, pTimer); + } + + /* callback */ + if ( !pTimer->fSpecificCpu + || pTimer->iCpu == curcpu) + pTimer->pfnTimer(pTimer, pTimer->pvUser, pTimer->iTick); + else + smp_rendezvous(NULL, rtTimerFreeBSDIpiAction, NULL, pvTimer); +} + + +RTDECL(uint32_t) RTTimerGetSystemGranularity(void) +{ + return 1000000000 / hz; /* ns */ +} + + +RTDECL(int) RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted) +{ + return VERR_NOT_SUPPORTED; +} + + +RTDECL(int) RTTimerReleaseSystemGranularity(uint32_t u32Granted) +{ + return VERR_NOT_SUPPORTED; +} + + +RTDECL(bool) RTTimerCanDoHighResolution(void) +{ + return false; +} + |