/* $Id: threadctxhooks-r0drv-solaris.c 48051 2013-08-26 09:51:43Z vboxsync $ */ /** @file * IPRT - Thread-Context Hook, Ring-0 Driver, Solaris. */ /* * Copyright (C) 2013 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-solaris-kernel.h" #include "internal/iprt.h" #include #include #include #include #include #include #include "internal/thread.h" /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * The internal thread-context object. */ typedef struct RTTHREADCTXINT { /** Magic value (RTTHREADCTXINT_MAGIC). */ uint32_t volatile u32Magic; /** The thread handle (owner) for which the context-hooks are registered. */ RTNATIVETHREAD hOwner; /** Pointer to the registered thread-context hook. */ PFNRTTHREADCTXHOOK pfnThreadCtxHook; /** User argument passed to the thread-context hook. */ void *pvUser; /** Whether this handle has any hooks registered or not. */ bool volatile fRegistered; /** Number of references to this object. */ uint32_t volatile cRefs; } RTTHREADCTXINT, *PRTTHREADCTXINT; /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** Validates a thread-context hook handle and returns rc if not valid. */ #define RTTHREADCTX_VALID_RETURN_RC(pThis, rc) \ do { \ AssertPtrReturn((pThis), (rc)); \ AssertReturn((pThis)->u32Magic == RTTHREADCTXINT_MAGIC, (rc)); \ AssertReturn((pThis)->cRefs > 0, (rc)); \ } while (0) /** * Hook function for the thread-preempting event. * * @param pvThreadCtxInt Opaque pointer to the internal thread-context * object. * * @remarks Called with the with preemption disabled! */ static void rtThreadCtxHooksSolPreempting(void *pvThreadCtxInt) { PRTTHREADCTXINT pThis = (PRTTHREADCTXINT)pvThreadCtxInt; AssertPtr(pThis); Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD)); if (pThis->fRegistered) { Assert(pThis->pfnThreadCtxHook); pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_PREEMPTING, pThis->pvUser); } } /** * Hook function for the thread-resumed event. * * @param pvThreadCtxInt Opaque pointer to the internal thread-context * object. */ static void rtThreadCtxHooksSolResumed(void *pvThreadCtxInt) { PRTTHREADCTXINT pThis = (PRTTHREADCTXINT)pvThreadCtxInt; AssertPtr(pThis); Assert(!RTThreadPreemptIsEnabled(NIL_RTTHREAD)); if (pThis->fRegistered) { Assert(pThis->pfnThreadCtxHook); pThis->pfnThreadCtxHook(RTTHREADCTXEVENT_RESUMED, pThis->pvUser); } } /** * Hook function for the thread-free event. * * @param pvThreadCtxInt Opaque pointer to the internal thread-context * object. * @param fIsExec Whether this event is triggered due to exec(). */ static void rtThreadCtxHooksSolFree(void *pvThreadCtxInt, int fIsExec) { PRTTHREADCTXINT pThis = (PRTTHREADCTXINT)pvThreadCtxInt; AssertPtrReturnVoid(pThis); AssertMsgReturnVoid(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis)); uint32_t cRefs = ASMAtomicReadU32(&pThis->cRefs); if (RT_UNLIKELY(!cRefs)) { /* Should never happen. */ AssertMsgFailed(("rtThreadCtxHooksSolFree with cRefs=0 pThis=%p\n", pThis)); return; } cRefs = ASMAtomicDecU32(&pThis->cRefs); if (!cRefs) { Assert(!pThis->fRegistered); ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXINT_MAGIC); RTMemFree(pThis); } } RTDECL(int) RTThreadCtxHooksCreate(PRTTHREADCTX phThreadCtx) { PRTTHREADCTXINT pThis; Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD)); pThis = (PRTTHREADCTXINT)RTMemAllocZ(sizeof(*pThis)); if (RT_UNLIKELY(!pThis)) return VERR_NO_MEMORY; pThis->u32Magic = RTTHREADCTXINT_MAGIC; pThis->hOwner = RTThreadNativeSelf(); pThis->fRegistered = false; pThis->cRefs = 2; /* One reference for the thread, one for the hook object. */ /* * installctx() allocates memory and thus cannot be used in RTThreadCtxHooksRegister() which can be used * with preemption disabled. We allocate the context-hooks here and use 'fRegistered' to determine if we can * invoke the consumer's hook or not. */ if (g_frtSolOldThreadCtx) { g_rtSolThreadCtx.Install.pfnSol_installctx_old(curthread, pThis, rtThreadCtxHooksSolPreempting, rtThreadCtxHooksSolResumed, NULL, /* fork */ NULL, /* lwp_create */ rtThreadCtxHooksSolFree); } else { g_rtSolThreadCtx.Install.pfnSol_installctx(curthread, pThis, rtThreadCtxHooksSolPreempting, rtThreadCtxHooksSolResumed, NULL, /* fork */ NULL, /* lwp_create */ NULL, /* exit */ rtThreadCtxHooksSolFree); } *phThreadCtx = pThis; return VINF_SUCCESS; } RTDECL(uint32_t) RTThreadCtxHooksRetain(RTTHREADCTX hThreadCtx) { PRTTHREADCTXINT pThis = hThreadCtx; RTTHREADCTX_VALID_RETURN_RC(hThreadCtx, UINT32_MAX); uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs); Assert(cRefs < UINT32_MAX / 2); return cRefs; } RTDECL(uint32_t) RTThreadCtxHooksRelease(RTTHREADCTX hThreadCtx) { PRTTHREADCTXINT pThis = hThreadCtx; if (pThis == NIL_RTTHREADCTX) return 0; RTTHREADCTX_VALID_RETURN_RC(hThreadCtx, UINT32_MAX); Assert(RTThreadPreemptIsEnabled(NIL_RTTHREAD)); ASMAtomicWriteBool(&pThis->fRegistered, false); uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs); if ( cRefs == 1 && pThis->hOwner == RTThreadNativeSelf()) { /* * removectx() will invoke rtThreadCtxHooksSolFree() and there is no way to bypass it and still use * rtThreadCtxHooksSolFree() at the same time. Hence the convulated reference counting. * * When this function is called from the owner thread and is the last reference, we call removectx() which * will invoke rtThreadCtxHooksSolFree() with cRefs = 1 and that will then free the hook object. * * When the function is called from a different thread, we simply decrement the reference. Whenever the * ring-0 thread dies, Solaris will call rtThreadCtxHooksSolFree() which will free the hook object. */ int rc; if (g_frtSolOldThreadCtx) { rc = g_rtSolThreadCtx.Remove.pfnSol_removectx_old(curthread, pThis, rtThreadCtxHooksSolPreempting, rtThreadCtxHooksSolResumed, NULL, /* fork */ NULL, /* lwp_create */ rtThreadCtxHooksSolFree); } else { rc = g_rtSolThreadCtx.Remove.pfnSol_removectx(curthread, pThis, rtThreadCtxHooksSolPreempting, rtThreadCtxHooksSolResumed, NULL, /* fork */ NULL, /* lwp_create */ NULL, /* exit */ rtThreadCtxHooksSolFree); } AssertMsg(rc, ("removectx() failed. rc=%d\n", rc)); NOREF(rc); #ifdef VBOX_STRICT cRefs = ASMAtomicReadU32(&pThis->cRefs); Assert(!cRefs); #endif cRefs = 0; } else if (!cRefs) { /* * The ring-0 thread for this hook object has already died. Free up the object as we have no more references. */ Assert(pThis->hOwner != RTThreadNativeSelf()); ASMAtomicWriteU32(&pThis->u32Magic, ~RTTHREADCTXINT_MAGIC); RTMemFree(pThis); } return cRefs; } RTDECL(int) RTThreadCtxHooksRegister(RTTHREADCTX hThreadCtx, PFNRTTHREADCTXHOOK pfnThreadCtxHook, void *pvUser) { /* * Validate input. */ PRTTHREADCTXINT pThis = hThreadCtx; if (pThis == NIL_RTTHREADCTX) return VERR_INVALID_HANDLE; AssertPtr(pThis); AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); Assert(pThis->hOwner == RTThreadNativeSelf()); /* * Register the callback. */ pThis->pvUser = pvUser; pThis->pfnThreadCtxHook = pfnThreadCtxHook; pThis->fRegistered = true; return VINF_SUCCESS; } RTDECL(int) RTThreadCtxHooksDeregister(RTTHREADCTX hThreadCtx) { /* * Validate input. */ PRTTHREADCTXINT pThis = hThreadCtx; if (pThis == NIL_RTTHREADCTX) return VERR_INVALID_HANDLE; AssertPtr(pThis); AssertMsgReturn(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis), VERR_INVALID_HANDLE); Assert(pThis->hOwner == RTThreadNativeSelf()); Assert(pThis->fRegistered); /* * Deregister the callback. */ pThis->fRegistered = false; return VINF_SUCCESS; } RTDECL(bool) RTThreadCtxHooksAreRegistered(RTTHREADCTX hThreadCtx) { /* * Validate input. */ PRTTHREADCTXINT pThis = hThreadCtx; if (pThis == NIL_RTTHREADCTX) return false; AssertPtr(pThis); AssertMsg(pThis->u32Magic == RTTHREADCTXINT_MAGIC, ("pThis->u32Magic=%RX32 pThis=%p\n", pThis->u32Magic, pThis)); return pThis->fRegistered; }