/******************************Module*Header*******************************\ * Module Name: os.cxx * * * * Convenient functions to access the OS interface. * * * * Created: 29-Aug-1989 19:59:05 * * Author: Charles Whitmer [chuckwh] * * * * Copyright (c) 1989-1999 Microsoft Corporation * \**************************************************************************/ #include "precomp.hxx" #include "muclean.hxx" #include "winstaw.h" extern BOOL G_fConsole; extern PFILE_OBJECT G_RemoteVideoFileObject; extern "C" USHORT gProtocolType; /******************************Public*Routine******************************\ * EngGetProcessHandle * * Returns the current thread of the application. * * History: * 24-Jan-1996 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ HANDLE APIENTRY EngGetProcessHandle() { return (HANDLE) NULL; } /******************************Public*Routine******************************\ * EngGetCurrentProcessId * * Returns the current process id of the application. * * History: * 28-May-1999 -by- Barton House [bhouse] * Wrote it. \**************************************************************************/ HANDLE APIENTRY EngGetCurrentProcessId() { return PsGetCurrentProcessId(); } /******************************Public*Routine******************************\ * EngGetCurrentThreadId * * Returns the current thread id of the application. * * History: * 28-May-1999 -by- Barton House [bhouse] * Wrote it. \**************************************************************************/ HANDLE APIENTRY EngGetCurrentThreadId() { return PsGetCurrentThreadId(); } #if !defined(_GDIPLUS_) // Kernel-mode version /******************************Public*Routine******************************\ * GreCreateSemaphore * * Create a semaphore with tracking. * * For use by GDI internal code. Tracking * allows semaphores to be released during MultiUserNtGreCleanup (Hydra) * cleanup. * * Warning! For code dealing with pool/semaphore tracking, as * in pooltrk.cxx and muclean.cxx, do not create semaphores with * this function, as it may call functions which use those semaphores. Instead, * use GreCreateSemaphoreNonTracked and GreDeleteSemaphoreNonTracked. * * Return Value: * * Handle for new semaphore or NULL * * History: * * 25-May-1995 - Changed to PERESOURCE * 19-May-1998 - Changed to HSEMAPHORE. * Merged AcquireGreResource with hsemCreateTracked * to create this function. * \**************************************************************************/ HSEMAPHORE GreCreateSemaphore() { return GreCreateSemaphoreInternal(OBJ_ENGINE_CREATED); } HSEMAPHORE GreCreateSemaphoreInternal(ULONG CreateFlag) { PERESOURCE pres; ULONGSIZE_T cj = sizeof(ERESOURCE); // // Adjust size to include ENGTRACKHDR header. // cj += sizeof(ENGTRACKHDR); // // Allocate ERESOURCE (must be nonpaged pool). // pres = (PERESOURCE) GdiAllocPoolNonPagedNS(cj, 'mesG'); if (pres) { // // Adjust resource to exclude the ENGTRACKHDR. // // Buffer // pethNew --> +----------------+ // | ENGTRACKHDR | // pres --> +----------------+ // | ERESOURCE | // | | // +----------------+ // // Note that pethNew always points to base of allocation. // ENGTRACKHDR *pethNew = (ENGTRACKHDR *) pres; pres = (PERESOURCE) (pethNew + 1); // // Initialize the resource. // if (NT_SUCCESS(ExInitializeResourceLite(pres))) { // // Track the resource. // if (CreateFlag & OBJ_DRIVER_CREATED) { MultiUserGreTrackAddEngResource(pethNew, ENGTRACK_DRIVER_SEMAPHORE); } else { MultiUserGreTrackAddEngResource(pethNew, ENGTRACK_SEMAPHORE); } } else { GdiFreePool(pethNew); pres = NULL; } } return (HSEMAPHORE) pres; } /******************************Public*Routine******************************\ * GreDeleteSemaphore * * Deletes the given semaphore. * \**************************************************************************/ VOID GreDeleteSemaphore( HSEMAPHORE hsem ) { PERESOURCE pres = (PERESOURCE) hsem; if (pres) { ENGTRACKHDR *pethVictim = (ENGTRACKHDR *) pres; // // Need to adjust peth pointer to header. // // Note: whether Hydra or non-Hydra, pethVictim will always // point to the base of the allocation. // // // Remove victim from tracking list. // pethVictim -= 1; MultiUserGreTrackRemoveEngResource(pethVictim); ASSERTGDI(pres->OwnerThreads[0].OwnerThread != (ERESOURCE_THREAD) PsGetCurrentThread(), "The resource is being deleted while it's still held!"); ExDeleteResourceLite(pres); GdiFreePool(pethVictim); } } /******************************Public*Routine******************************\ * GreCreateSemaphoreNonTracked * * Create a semaphore, without pool-tracking or semaphore-tracking. * * Only for use by the pool-tracking and semaphore-tracking code. This * avoids the circular dependency which using GreCreateSemaphore would * cause. * * Return Value: * * Handle for new semaphore or NULL * \**************************************************************************/ HSEMAPHORE GreCreateSemaphoreNonTracked() { PERESOURCE pres; pres = (PERESOURCE) ExAllocatePoolWithTag( (POOL_TYPE)NonPagedPool, sizeof(ERESOURCE), 'mesG'); if (pres) { if (!NT_SUCCESS(ExInitializeResourceLite(pres))) { ExFreePool(pres); pres = NULL; } } return (HSEMAPHORE) pres; } /******************************Public*Routine******************************\ * GreDeleteSemaphoreNonTracked * * Deletes the given non-tracked semaphore. * \**************************************************************************/ VOID GreDeleteSemaphoreNonTracked( HSEMAPHORE hsem ) { PERESOURCE pres = (PERESOURCE) hsem; if (pres) { ASSERTGDI(pres->OwnerThreads[0].OwnerThread != (ERESOURCE_THREAD) PsGetCurrentThread(), "The resource is being deleted while it's still held!"); ExDeleteResourceLite(pres); ExFreePool(pres); } } #ifdef VALIDATE_LOCKS BOOL gDebugSem = TRUE; BOOL gDebugSemBreak = FALSE; #define SEM_HISTORY_LENGTH 4 typedef struct { const char *name; const char *func; const char *file; int line; } SemHistory; typedef struct SemEntry { HSEMAPHORE hsem; ULONG count; ULONG order; HSEMAPHORE parent; #if SEM_HISTORY_LENGTH SemHistory Acquired[SEM_HISTORY_LENGTH]; #endif } SemEntry; #define kMaxSemEntries 64 typedef struct SemTable { FLONG flags; ULONG numEntries; SemEntry entries[kMaxSemEntries]; } SemTable; /******************************Public*Routine******************************\ * SemTrace::SemTrace * \**************************************************************************/ SemTrace::SemTrace(FLONG SetFlags) { DontClearMask = ~0; if (gDebugSem && gDebugSemBreak) { pThread = W32GetCurrentThread(); if(pThread != NULL) { SemTable *pSemTable = (SemTable *) pThread->pSemTable; if(pSemTable != NULL) { DontClearMask = pSemTable->flags | ~SetFlags; if (DontClearMask != ~0) { DbgPrint(" ** Enabling additional sem tracing for W32THREAD @ %p, SemTable @ %p\n", pThread, pSemTable); pSemTable->flags |= SetFlags; } } } } } /******************************Public*Routine******************************\ * SemTrace::~SemTrace * \**************************************************************************/ SemTrace::~SemTrace() { // Have anything to clear? if (DontClearMask != ~0) { SemTable *pSemTable = (SemTable *)pThread->pSemTable; if (pSemTable) { DbgPrint(" ** Disabling additional sem tracing for W32THREAD @ %p, SemTable @ %p\n", pThread, pSemTable); pSemTable->flags &= DontClearMask; } else { DbgPrint(" ** Couldn't disable added sem tracing for W32THREAD @ %p; SemTable is NULL\n", pThread); } } } /*****************************Private*Routine******************************\ * FindSemEntry * \**************************************************************************/ SemEntry * FindSemEntry(SemTable * pTable, HSEMAPHORE hsem) { SemEntry * entry = pTable->entries; SemEntry * sentry = entry + pTable->numEntries; while(entry < sentry) { if(entry->hsem == hsem) return entry; entry++; } return NULL; } /*****************************Private*Routine******************************\ * RemoveSemEntry * \**************************************************************************/ void RemoveSemEntry( SemTable *pTable, SemEntry *entry, const char *name, const char *func, const char *file, int line ) { SemEntry *sentry; SemEntry OldEntry; ASSERTGDI(pTable->numEntries > 0, "numEntries is invalid"); ASSERTGDI(entry < pTable->entries + pTable->numEntries, "entry is invalid"); OldEntry = *entry; pTable->numEntries--; sentry = &pTable->entries[pTable->numEntries]; if (entry != sentry) { DbgPrint("Locks released out of acquisition order\n"); DbgPrint(" Now releasing order %4d %s (%X) in %s @ %s:%d\n", entry->order, name, entry->hsem, func, file, line); while (entry < sentry) { *entry = *(entry + 1); #if SEM_HISTORY_LENGTH DbgPrint(" Warning: Still holding order %4d %s (%X) %u times\n" " First acquired in %s @ %s:%d\n", entry->order, entry->Acquired[0].name, entry->hsem, entry->count, entry->Acquired[0].func, entry->Acquired[0].file, entry->Acquired[0].line ); #else DbgPrint(" Warning: Still holding order %4d %s (%X) %u times\n", entry->order, entry->hsem, entry->count ); #endif if (OldEntry.hsem == entry->parent) { DbgPrint(" * Error: Releasing parent before child.\n"); } else if (OldEntry.parent == entry->parent && OldEntry.order < entry->order) { DbgPrint(" * Error: Higher order semaphore is still held.\n"); } entry++; } if (gDebugSemBreak) { DbgBreakPoint(); } } if (pTable->flags & ST_SAVE_RELEASES) { // Maintain a history of released locks // sentry = &pTable->entries[kMaxSemEntries-1]; while(entry < sentry) { *entry = *(entry + 1); entry++; } *sentry = OldEntry; } } /******************************Public*Routine******************************\ * GreReleaseSemaphoreAndValidate * * * * Call via GreAcquireSemaphoreEx with VALIDATE_LOCKS enabled. * \**************************************************************************/ VOID GreAcquireSemaphoreAndValidate( HSEMAPHORE hsem, ULONG order, HSEMAPHORE parent, const char *name, const char *func, const char *file, int line ) { GDIFunctionID(GreAcquireSemaphoreAndValidate); GreAcquireSemaphore(hsem); if(gDebugSem) { PW32THREAD pThread = W32GetCurrentThread(); if(pThread != NULL) { SemTable *pSemTable = (SemTable *) pThread->pSemTable; if(pSemTable == NULL) { pThread->pSemTable = PALLOCMEM(sizeof(SemTable), 'dtdG'); pSemTable = (SemTable *) pThread->pSemTable; if(pSemTable != NULL) { pThread->pSemTable = (PVOID)pSemTable; pSemTable->flags = ST_DEFAULT; pSemTable->numEntries = 0; } } if(pSemTable != NULL) { SemEntry *pSemEntry = FindSemEntry(pSemTable, hsem); if(pSemEntry != NULL) { if (pSemTable->flags & ST_VERBOSE) { DbgPrint("SemTrace: Reacquire (%d) order %4d %24s (%X) in %s @ %s:%d\n", pSemEntry->count, order, name, hsem, func, file, line); } if (order != pSemEntry->order) { DbgPrint("* Different order specification (%d) for %24s (%X) in %s @ %s:%d\n" " Originally acquired with order %d as %s in %s @ %s:%d (%d acquisitions)\n", order, name, hsem, func, file, line, pSemEntry->order, pSemEntry->Acquired[0].name, pSemEntry->Acquired[0].func, pSemEntry->Acquired[0].file, pSemEntry->Acquired[0].line, pSemEntry->count ); } #if SEM_HISTORY_LENGTH > 1 if (pSemEntry->count < SEM_HISTORY_LENGTH) { pSemEntry->Acquired[pSemEntry->count].name = name; pSemEntry->Acquired[pSemEntry->count].func = func; pSemEntry->Acquired[pSemEntry->count].file = file; pSemEntry->Acquired[pSemEntry->count].line = line; } #endif pSemEntry->count++; } else { if (pSemTable->flags & ST_VERBOSE) { DbgPrint("SemTrace: Now acquiring order %4d %24s (%X) in %s @ %s:%d\n", order, name, hsem, func, file, line); } ASSERTGDI(pSemTable->numEntries < kMaxSemEntries, "too many entries"); pSemEntry = &pSemTable->entries[pSemTable->numEntries++]; pSemEntry->hsem = hsem; pSemEntry->order = order; pSemEntry->parent = parent; pSemEntry->count = 1; #if SEM_HISTORY_LENGTH pSemEntry->Acquired[0].name = name; pSemEntry->Acquired[0].func = func; pSemEntry->Acquired[0].file = file; pSemEntry->Acquired[0].line = line; #endif // Check order if(parent != NULL) { if(FindSemEntry(pSemTable, parent) == NULL) { DbgPrint("Parent semaphore not acquired"); if (gDebugSemBreak) { DbgBreakPoint(); } } } SemEntry * entry = pSemTable->entries; SemEntry * sentry = entry + pSemTable->numEntries; BOOL Misordered = FALSE; while(entry < sentry) { if(entry->parent == parent && entry->order > order) { if (!Misordered) { DbgPrint("Locks obtained out of order\n"); if (!(pSemTable->flags & ST_VERBOSE)) { DbgPrint(" Now acqquiring order %4d %24s (%X) in %s @ %s:%d\n", order, name, hsem, func, file, line); } Misordered = TRUE; } #if SEM_HISTORY_LENGTH DbgPrint(" Conflicts with order %4d %24s (%X) first acquired in %s @ %s:%d with %d acquisitions\n", entry->order, entry->Acquired[0].name, entry->hsem, entry->Acquired[0].func, entry->Acquired[0].file, entry->Acquired[0].line, entry->count ); #else DbgPrint(" Conflicts with order %4d (%X)\n", entry->order, entry->hsem ); #endif } entry++; } if (Misordered && gDebugSemBreak) { DbgBreakPoint(); } } } } } } /******************************Public*Routine******************************\ * GreReleaseSemaphoreAndValidate * * * * Call via GreReleaseSemaphoreEx with VALIDATE_LOCKS enabled. * \**************************************************************************/ VOID GreReleaseSemaphoreAndValidate( HSEMAPHORE hsem, const char *name, const char *func, const char *file, int line ) { GDIFunctionID(GreReleaseSemaphoreAndValidate); if(gDebugSem) { PW32THREAD pThread = W32GetCurrentThread(); if(pThread != NULL) { SemTable * pSemTable = (SemTable *) pThread->pSemTable; if(pSemTable != NULL) { SemEntry * pSemEntry = FindSemEntry(pSemTable, hsem); ASSERTGDI(pSemEntry != NULL, "error finding sem"); if(pSemEntry != NULL) { pSemEntry->count--; if (pSemTable->flags & ST_VERBOSE) { if (pSemEntry->count) { DbgPrint("TraceSem: Releasing (%d) order %4d %24s (%X) in %s @ %s:%d\n", pSemEntry->count, pSemEntry->order, name, hsem, func, file, line); } else { DbgPrint("TraceSem: Fully release order %4d %24s (%X) in %s @ %s:%d\n", pSemEntry->order, name, hsem, func, file, line); } } #if SEM_HISTORY_LENGTH > 1 if (pSemTable->flags & ST_SAVE_RELEASES) { // Maintain a history of releases // SemHistory *entry = &pSemEntry->Acquired[pSemEntry->count]; SemHistory *sentry = &pSemEntry->Acquired[SEM_HISTORY_LENGTH-1]; SemHistory OldAcquisition = *entry; while(entry < sentry) { *entry = *(entry + 1); entry++; } *sentry = OldAcquisition; } #endif if(pSemEntry->count == 0) { RemoveSemEntry(pSemTable, pSemEntry, name, func, file, line); } } } } } GreReleaseSemaphore(hsem); } #endif /******************************Public*Routine******************************\ * GreAcquireSemaphore * \**************************************************************************/ VOID FASTCALL GreAcquireSemaphore( HSEMAPHORE hsem ) { // // This if is here for cleanup code // Generic cleanup code needs to // acquire the semaphore, but in some cases // the semaphore either hasn't been created or it // has been thrown away already. // if (hsem) { KeEnterCriticalRegion(); ExAcquireResourceExclusiveLite((PERESOURCE) hsem, TRUE); } else { #if DBG if (G_fConsole) { RIP("Tried to acquire a non-existant or deleted semaphore\n"); } else { WARNING("Tried to acquire a non-existant or deleted semaphore\n"); } #endif } } /******************************Public*Routine******************************\ * GreAcquireSemaphoreShared * \**************************************************************************/ VOID FASTCALL GreAcquireSemaphoreShared( HSEMAPHORE hsem ) { // // This if is here for cleanup code // Generic cleanup code needs to // acquire the semaphore, but in some cases // the semaphore either hasn't been created or it // has been thrown away already. // if (hsem) { KeEnterCriticalRegion(); ExAcquireResourceSharedLite((PERESOURCE) hsem, TRUE); } else { #if DBG if (G_fConsole) { RIP("Tried to acquire a non-existant or deleted semaphore\n"); } else { WARNING("Tried to acquire a non-existant or deleted semaphore\n"); } #endif } } /******************************Public*Routine******************************\ * GreReleaseSemaphore * \**************************************************************************/ VOID FASTCALL GreReleaseSemaphore( HSEMAPHORE hsem ) { // // This if is here for cleanup code // Generic cleanup code needs to // acquire the semaphore, but in some cases // the semaphore either hasn't been created or it // has been thrown away already. // if (hsem) { ExReleaseResourceLite((PERESOURCE) hsem); KeLeaveCriticalRegion(); } else { #if DBG if (G_fConsole) { RIP("Tried to release a non-existant or deleted semaphore\n"); } else { WARNING("Tried to release a non-existant or deleted semaphore\n"); } #endif } } /******************************Public*Routine******************************\ * GreIsSemaphoreOwned * * * * Returns TRUE if the semaphore is currently held. * * * \**************************************************************************/ BOOL GreIsSemaphoreOwned( HSEMAPHORE hsem ) { return ((PERESOURCE) hsem)->ActiveCount != 0; } /******************************Public*Routine******************************\ * GreIsSemaphoreOwnedByCurrentThread * * * * Returns TRUE if the current thread owns the semaphore, FALSE * * otherwise. * * * \**************************************************************************/ BOOL GreIsSemaphoreOwnedByCurrentThread( HSEMAPHORE hsem ) { return ((PERESOURCE) hsem)->OwnerThreads[0].OwnerThread == (ERESOURCE_THREAD) PsGetCurrentThread(); } /******************************Public*Routine******************************\ * GreIsSemaphoreSharedByCurrentThread * * * * Returns TRUE if the current thread owns the semaphore, FALSE * * otherwise. * * * \**************************************************************************/ BOOL GreIsSemaphoreSharedByCurrentThread( HSEMAPHORE hsem ) { return ExIsResourceAcquiredSharedLite((PERESOURCE) hsem); } /******************************Public*Routine******************************\ * GreCreateFastMutex * * * * Creates a fast mutex. Exactly like a semaphore, except it is not * * reentrant. Fast mutexes are not tracked either (since they do not need * * to be destroyed by the kernel.) Their pool may be tracked. * * * \**************************************************************************/ HFASTMUTEX GreCreateFastMutex() { PFAST_MUTEX pfm; pfm = (PFAST_MUTEX) GdiAllocPoolNonPagedNS(sizeof(FAST_MUTEX), 'msfG'); if (pfm) { ExInitializeFastMutex(pfm); } return (HFASTMUTEX) pfm; } /******************************Public*Routine******************************\ * GreDeleteFastMutex * * * \**************************************************************************/ VOID GreDeleteFastMutex( HFASTMUTEX hfm ) { if (hfm) { GdiFreePool(hfm); } } /******************************Public*Routine******************************\ * GreAcquireFastMutex * * * \**************************************************************************/ VOID GreAcquireFastMutex( HFASTMUTEX hfm ) { KeEnterCriticalRegion(); ExAcquireFastMutex((PFAST_MUTEX) hfm); } /******************************Public*Routine******************************\ * GreReleaseFastMutex * * * \**************************************************************************/ VOID GreReleaseFastMutex( HFASTMUTEX hfm ) { ExReleaseFastMutex((PFAST_MUTEX) hfm); KeLeaveCriticalRegion(); } #else // _GDIPLUS_ // User-mode version /******************************Public*Routine******************************\ * GreCreateSemaphore * * Create a semaphore with tracking. * * For use by GDI internal code. Tracking * allows semaphores to be released during MultiUserNtGreCleanup * cleanup. * * Warning! For code dealing with pool/semaphore tracking, as * in pooltrk.cxx and muclean.cxx, do not create semaphores with * this function, as it may call functions which use those semaphores. Instead, * use GreCreateSemaphoreNonTracked and GreDeleteSemaphoreNonTracked. * * Return Value: * * Handle for new semaphore or NULL * \**************************************************************************/ HSEMAPHORE GreCreateSemaphore() { return GreCreateSemaphoreInternal(OBJ_ENGINE_CREATED); } HSEMAPHORE GreCreateSemaphoreInternal(ULONG CreateFlag) { LPCRITICAL_SECTION pcs; SIZE_T cj = sizeof(CRITICAL_SECTION); // // Adjust size to include ENGTRACKHDR header. // >> cj += sizeof(ENGTRACKHDR); pcs = (LPCRITICAL_SECTION) RtlAllocateHeap(RtlProcessHeap(), 0, == sizeof(CRITICAL_SECTION)); if (pcs) { // // Adjust semaphore to exclude the ENGTRACKHDR. // // Buffer // pethNew --> +------------------+ // | ENGTRACKHDR | // pcs --> +------------------+ // | CRITICAL_SECTION | // | | // +------------------+ // // Note that pethNew always points to base of allocation. // ENGTRACKHDR *pethNew = (ENGTRACKHDR *) pcs; >> pcs = (LPCRITICAL_SECTION) (pethNew + 1); == // // Initialize the semaphore. // InitializeCriticalSection(pcs); // // Track the semaphore // >> if (CreateFlag & OBJ_DRIVER_CREATED) { MultiUserGreTrackAddEngResource(pethNew, ENGTRACK_DRIVER_SEMAPHORE); } else { MultiUserGreTrackAddEngResource(pethNew, ENGTRACK_SEMAPHORE); } == } return (HSEMAPHORE) pcs; } /******************************Public*Routine******************************\ * GreDeleteSemaphore * * Deletes the given semaphore. * \**************************************************************************/ VOID GreDeleteSemaphore( HSEMAPHORE hsem ) { LPCRITICAL_SECTION pcs = (LPCRITICAL_SECTION) hsem; if (pcs) { ENGTRACKHDR *pethVictim = (ENGTRACKHDR *) pcs; // // Need to adjust peth pointer to header. // // Note: whether Hydra or non-Hydra, pethVictim will always // point to the base of the allocation. // >> // // Remove victim from tracking list. // pethVictim -= 1; MultiUserGreTrackRemoveEngResource(pethVictim); == DeleteCriticalSection(pcs); RtlFreeHeap(RtlProcessHeap(), 0, pethVictim); } } /******************************Public*Routine******************************\ * GreCreateSemaphoreNonTracked * * Create a semaphore, without pool-tracking or semaphore-tracking. * * Only for use by the pool-tracking and semaphore-tracking code. This * avoids the circular dependency which using GreCreateSemaphore would * cause. * * Return Value: * * Handle for new semaphore or NULL * \**************************************************************************/ HSEMAPHORE GreCreateSemaphoreNonTracked() { LPCRITICAL_SECTION pcs; pcs = (LPCRITICAL_SECTION) RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(CRITICAL_SECTION)); if (pcs) { // // Initialize the semaphore. // InitializeCriticalSection(pcs); } return (HSEMAPHORE) pcs; } /******************************Public*Routine******************************\ * GreDeleteSemaphoreNonTracked * * Deletes the given non-tracked semaphore. * \**************************************************************************/ VOID GreDeleteSemaphoreNonTracked( HSEMAPHORE hsem ) { LPCRITICAL_SECTION pcs = (LPCRITICAL_SECTION) hsem; if (pcs) { DeleteCriticalSection(pcs); RtlFreeHeap(RtlProcessHeap(), 0, pcs); } } /******************************Public*Routine******************************\ * GreAcquireSemaphore * \**************************************************************************/ VOID GreAcquireSemaphore( HSEMAPHORE hsem ) { EnterCriticalSection((LPCRITICAL_SECTION) hsem); } /******************************Public*Routine******************************\ * GreReleaseSemaphore * \**************************************************************************/ VOID GreReleaseSemaphore( HSEMAPHORE hsem ) { LeaveCriticalSection((LPCRITICAL_SECTION) hsem); } /******************************Public*Routine******************************\ * GreIsSemaphoreOwned * * * * Returns TRUE if the semaphore is currently held. * * * \**************************************************************************/ BOOL GreIsSemaphoreOwned( HSEMAPHORE hsem ) { return ((RTL_CRITICAL_SECTION *) hsem)->LockCount != -1; } /******************************Public*Routine******************************\ * GreIsSemaphoreOwnedByCurrentThread * * * * Returns TRUE if the current thread owns the semaphore, FALSE * * otherwise. * * * \**************************************************************************/ BOOL GreIsSemaphoreOwnedByCurrentThread( HSEMAPHORE hsem ) { return ((RTL_CRITICAL_SECTION *) hsem)->OwningThread == (HANDLE) GetCurrentThreadId(); } /******************************Public*Routine******************************\ * GreCreateFastMutex * * * * Creates a fast mutex. In this, the user mode version, it is exactly * * the same as a non-tracked semaphore. * * \**************************************************************************/ HFASTMUTEX GreCreateFastMutex() { LPCRITICAL_SECTION pcs; pcs = (LPCRITICAL_SECTION) RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(CRITICAL_SECTION)); if (pcs) { // // Initialize the semaphore. // InitializeCriticalSection(pcs); } return (HFASTMUTEX) pcs; } /******************************Public*Routine******************************\ * GreDeleteFastMutex * * * \**************************************************************************/ VOID GreDeleteFastMutex( HFASTMUTEX hfm ) { LPCRITICAL_SECTION pcs = (LPCRITICAL_SECTION) hfm; if (pcs) { DeleteCriticalSection(pcs); RtlFreeHeap(RtlProcessHeap(), 0, pcs); } } /******************************Public*Routine******************************\ * GreAcquireFastMutex * * * \**************************************************************************/ VOID GreAcquireFastMutex( HFASTMUTEX hfm ) { EnterCriticalSection((LPCRITICAL_SECTION) hfm); } /******************************Public*Routine******************************\ * GreReleaseFastMutex * * * \**************************************************************************/ VOID GreReleaseFastMutex( HFASTMUTEX hfm ) { LeaveCriticalSection((LPCRITICAL_SECTION) hfm); } #endif // _GDIPLUS_ /******************************Public*Routines*****************************\ * GreAcquireHmgrSemaphore * * GreReleaseHmgrSemaphore * * * * Convenience functions for the handle manager semaphore. * * * \**************************************************************************/ VOID GreAcquireHmgrSemaphore() { GDIFunctionID(GreAcquireHmgrSemaphore); GreAcquireSemaphoreEx(ghsemHmgr, SEMORDER_HMGR, NULL); } VOID GreReleaseHmgrSemaphore() { GDIFunctionID(GreReleaseHmgrSemaphore); GreReleaseSemaphoreEx(ghsemHmgr); } /******************************Public*Routine******************************\ * EngCreateSemaphore() * * History: * 22-Feb-1995 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ HSEMAPHORE EngCreateSemaphore( VOID ) { return GreCreateSemaphoreInternal(OBJ_DRIVER_CREATED); } VOID EngAcquireSemaphore( HSEMAPHORE hsem ) { GreAcquireSemaphore(hsem); W32THREAD * pThread = W32GetCurrentThread(); #ifdef CHECK_SEMAPHORE_USAGE pThread->dwEngAcquireCount++; #endif } VOID EngReleaseSemaphore( HSEMAPHORE hsem ) { W32THREAD * pThread = W32GetCurrentThread(); #ifdef CHECK_SEMAPHORE_USAGE pThread->dwEngAcquireCount--; #endif GreReleaseSemaphore(hsem); } #ifdef CHECK_SEMAPHORE_USAGE VOID GreCheckSemaphoreUsage( VOID ) { W32THREAD * pThread = W32GetCurrentThread(); ASSERTGDI(pThread->dwEngAcquireCount == 0, "EngAcquireCount non-zero\n"); } #endif VOID EngDeleteSemaphore( HSEMAPHORE hsem ) { GreDeleteSemaphore(hsem); } BOOL EngIsSemaphoreOwned( HSEMAPHORE hsem ) { return(GreIsSemaphoreOwned(hsem)); } BOOL EngIsSemaphoreOwnedByCurrentThread( HSEMAPHORE hsem ) { return(GreIsSemaphoreOwnedByCurrentThread(hsem)); } /******************************Public*Routine******************************\ * * EngInitializeSafeSemaphore * EngDeleteSafeSemaphore * * Manages semaphore lifetime in a reference-counted thread-safe manner. * * History: * Wed Apr 16 18:23:40 1997 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL EngInitializeSafeSemaphore(ENGSAFESEMAPHORE *pssem) { // Do create/destroy inside the handle manager global lock // as a convenient way to synchronize them. MLOCKFAST mlf; ASSERTGDI(pssem->lCount >= 0, "InitSafeSem: bad lCount\n"); if (pssem->lCount == 0) { ASSERTGDI(pssem->hsem == NULL, "InitSafeSem: overwriting hsem\n"); pssem->hsem = GreCreateSemaphoreInternal(OBJ_DRIVER_CREATED); if (pssem->hsem == NULL) { return FALSE; } } pssem->lCount++; return TRUE; } void EngDeleteSafeSemaphore(ENGSAFESEMAPHORE *pssem) { // Do create/destroy inside the handle manager global lock // as a convenient way to synchronize them. MLOCKFAST mlf; ASSERTGDI(pssem->lCount >= 1, "DeleteSafeSem: lCount underflow\n"); if (pssem->lCount == 1) { ASSERTGDI(pssem->hsem != NULL, "DeleteSafeSem: No hsem\n"); GreDeleteSemaphore(pssem->hsem); pssem->hsem = NULL; } pssem->lCount--; } /******************************Public*Routine******************************\ * EngSetLastError * * Saves Error code passed in. * * History: * Sat 31-Oct-1992 -by- Patrick Haluptzok [patrickh] * Remove wrapper. * * 28-Oct-1992 -by- Bodin Dresevic [BodinD] * Wrote it. \**************************************************************************/ VOID EngSetLastError(ULONG iError) { // // Warning: NtCurrentTeb() accesses the TEB structure via the // KPCR structure. However, during session shutdown, the // TEB ptr is set to zero in the TCB, but not the KPCR. So if // code is callable during session shutdown, cannot invoke // NtCurrentTeb. Use KeGetCurrentThread()->Teb instead. // PTEB pteb = (PTEB) PsGetCurrentThreadTeb(); if (pteb) pteb->LastErrorValue = iError; #if DBG PSZ psz; switch (iError) { case ERROR_INVALID_HANDLE: psz = "ERROR_INVALID_HANDLE"; break; case ERROR_NOT_ENOUGH_MEMORY: psz = "ERROR_NOT_ENOUGH_MEMORY"; break; case ERROR_INVALID_PARAMETER: psz = "ERROR_INVALID_PARAMETER"; break; case ERROR_BUSY: psz = "ERROR_BUSY"; break; case ERROR_ARITHMETIC_OVERFLOW: psz = "ERROR_ARITHMETIC_OVERFLOW"; break; case ERROR_INVALID_FLAGS: psz = "ERROR_INVALID_FLAGS"; break; case ERROR_CAN_NOT_COMPLETE: psz = "ERROR_CAN_NOT_COMPLETE"; break; default: psz = "unknown error code"; break; } // DbgPrint("GRE Err: %s = 0x%04X\n", psz, (USHORT) iError); #endif } /******************************Public*Routine******************************\ * * History: * 27-Jun-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ ULONG APIENTRY EngGetLastError() { ULONG ulError = 0; // // Warning: NtCurrentTeb() accesses the TEB structure via the // KPCR structure. However, during session shutdown, the // TEB ptr is set to zero in the TCB, but not the KPCR. So if // code is callable during session shutdown, cannot invoke // NtCurrentTeb. Use KeGetCurrentThread()->Teb instead. // PTEB pteb = (PTEB) PsGetCurrentThreadTeb(); if (pteb) ulError = pteb->LastErrorValue; return(ulError); } /******************************Public*Routine******************************\ * GreLockDisplay() * * History: * 01-Nov-1994 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ VOID APIENTRY GreLockDisplay( HDEV hdev ) { GDIFunctionID(GreLockDisplay); PDEVOBJ pdo(hdev); GreAcquireSemaphoreEx(pdo.hsemDevLock(), SEMORDER_DEVLOCK, NULL); GreEnterMonitoredSection(pdo.ppdev, WD_DEVLOCK); } /******************************Public*Routine******************************\ * GreUnlockDisplay() * * History: * 01-Nov-1994 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ VOID APIENTRY GreUnlockDisplay( HDEV hdev ) { GDIFunctionID(GreUnlockDisplay); PDEVOBJ pdo(hdev); GreExitMonitoredSection(pdo.ppdev, WD_DEVLOCK); GreReleaseSemaphoreEx(pdo.hsemDevLock()); } #if DBG /******************************Public*Routine******************************\ * GreIsDisplayLocked() * * History: * 10-Jun-1998 -by- Lingyun Wang [lingyunw] * Wrote it. \**************************************************************************/ BOOL APIENTRY GreIsDisplayLocked( HDEV hdev ) { PDEVOBJ pdo(hdev); if (GreIsSemaphoreOwnedByCurrentThread(pdo.hsemDevLock())) { return (TRUE); } else { return (FALSE); } } #endif /***************************************************************************\ * EngDebugPrint * * History: * 02-Feb-1995 -by- Andre Vachon [andreva] * Wrote it. \***************************************************************************/ VOID EngDebugPrint( PCHAR StandardPrefix, PCHAR DebugMessage, va_list ap ) { char buffer[256]; int len; // // We prepend the STANDARD_DEBUG_PREFIX to each string, and // append a new-line character to the end: // DbgPrint(StandardPrefix); vsprintf(buffer, DebugMessage, ap); DbgPrint(buffer); } /******************************Public*Routine******************************\ * EngDebugBreak() * * History: * 16-Feb-1995 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ VOID APIENTRY EngDebugBreak( VOID ) { DbgBreakPoint(); } /******************************Public*Routine******************************\ * EngDebugBreak() * * History: * 7-Dec-2001 -by- Jonathan Schwartz [JSchwart] * Wrote it. \**************************************************************************/ VOID APIENTRY EngBugCheckEx( IN ULONG BugCheckCode, IN ULONG_PTR P1, IN ULONG_PTR P2, IN ULONG_PTR P3, IN ULONG_PTR P4 ) { KeBugCheckEx(BugCheckCode, P1, P2, P3, P4); } /******************************Public*Routine******************************\ * EngAllocMem() * * History: * 27-May-1995 -by- Tom Zakrajsek [tomzak] * Added a flags parameter to allow zeroing of memory. * * 02-Feb-1995 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ PVOID EngAllocMem( ULONG fl, ULONG cj, ULONG tag ) { PVOID pvRet; // // Don't trust the driver to only ask for non-zero length buffers. // if (cj == 0) return(NULL); // // Adjust size to include ENGTRACKHDR header. // // Sundown note: sizeof(ENGTRACKHDR) will fit in 32-bit, so ULONG cast OK // if (cj <= (MAXULONG - sizeof(ENGTRACKHDR))) cj += ((ULONG) sizeof(ENGTRACKHDR)); else return(NULL); if (cj >= (PAGE_SIZE * 10000)) { WARNING("EngAllocMem: temp buffer >= 10000 pages"); return(NULL); } if (fl & FL_NONPAGED_MEMORY) { pvRet = GdiAllocPoolNonPaged(cj,tag); } else { pvRet = GdiAllocPool(cj,tag); } if (fl & FL_ZERO_MEMORY) { if (pvRet) { RtlZeroMemory(pvRet,cj); } } if (pvRet) { // // Add allocation to the tracking list. // ENGTRACKHDR *pethNew = (ENGTRACKHDR *) pvRet; MultiUserGreTrackAddEngResource(pethNew, ENGTRACK_ALLOCMEM); // // Adjust return pointer to hide the LIST_ENTRY header. // pvRet = (PVOID) (pethNew + 1); } return pvRet; } /******************************Public*Routine******************************\ * EngFreeMem() * * History: * 02-Feb-1995 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ VOID EngFreeMem( PVOID pv ) { if (pv) { // // Remove victim from tracking list. // ENGTRACKHDR *pethVictim = ((ENGTRACKHDR *) pv) - 1; MultiUserGreTrackRemoveEngResource(pethVictim); // // Adjust pointer to base of allocation. // pv = (PVOID) pethVictim; VFREEMEM(pv); } return; } /******************************Public*Routine******************************\ * EngProbeForRead() * * History: * 02-Oct-1995 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ VOID EngProbeForRead( PVOID Address, ULONG Length, ULONG Alignment ) { ProbeForRead(Address, Length, Alignment); } /******************************Public*Routine******************************\ * GDIEngUserMemAllocNodeCompare() * * History: * 18-Sep-2001 -by- Pravin Santiago [pravins] * Wrote it. \**************************************************************************/ RTL_GENERIC_COMPARE_RESULTS GDIEngUserMemAllocNodeCompare(RTL_AVL_TABLE *Table, PVOID FirstStruct, PVOID SecondStruct) { PGDIENGUSERMEMALLOCNODE p1 = (PGDIENGUSERMEMALLOCNODE)FirstStruct; PGDIENGUSERMEMALLOCNODE p2 = (PGDIENGUSERMEMALLOCNODE)SecondStruct; return (p1->pUserMem > p2->pUserMem ) ? GenericGreaterThan : (p1->pUserMem < p2->pUserMem ) ? GenericLessThan : GenericEqual; } /******************************Public*Routine******************************\ * GDIEngUserMemAllocNodeAlloc() * * History: * 18-Sep-2001 -by- Pravin Santiago [pravins] * Wrote it. \**************************************************************************/ PVOID GDIEngUserMemAllocNodeAlloc(RTL_AVL_TABLE *Table, CLONG Size) { return PALLOCNOZ(Size,'amUG'); } /******************************Public*Routine******************************\ * GDIEngUserMemAllocNodeFree() * * History: * 18-Sep-2001 -by- Pravin Santiago [pravins] * Wrote it. \**************************************************************************/ VOID GDIEngUserMemAllocNodeFree(RTL_AVL_TABLE *Table, PVOID Buffer) { VFREEMEM(Buffer); } /******************************Public*Routine******************************\ * EngAllocUserMem() * * This routine allocates a piece of memory for USER mode and locks it * down. A driver must be very careful with this memory as it is only * valid for this process. * * History: * 10-Sep-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ PVOID EngAllocUserMem( SIZE_T cj, //ZwAllocateVirtualMemory uses SIZE_T, change accordingly ULONG tag ) { NTSTATUS status; PVOID pv = NULL; HANDLE hSecure; // // Don't trust the driver to only ask for non-zero length buffers. // if (cj == 0) return(NULL); status = ZwAllocateVirtualMemory( NtCurrentProcess(), &pv, 0, &cj, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (NT_SUCCESS(status)) { hSecure = MmSecureVirtualMemory(pv,cj,PAGE_READWRITE); if (hSecure) { // Add the new allocation to the Process's GDIEngUserMemAllocTable PW32PROCESS pW32Process = W32GetCurrentProcess(); GDIENGUSERMEMALLOCNODE tmpNode; PGDIENGUSERMEMALLOCNODE pNewNode = 0; BOOLEAN bNew = FALSE; tmpNode.pUserMem = pv; tmpNode.hSecure = hSecure; tmpNode.cj = cj; SIMPLELOCK sl(&pW32Process->GDIEngUserMemAllocTableLock); pNewNode = (PGDIENGUSERMEMALLOCNODE) RtlInsertElementGenericTableAvl(&pW32Process->GDIEngUserMemAllocTable, &tmpNode, sizeof(tmpNode), &bNew); ASSERTGDI((pNewNode && !bNew) != TRUE, "EngAllocUserMem: (pNewNode && !bNew) == TRUE\n"); if (pNewNode == 0 || bNew == FALSE) { MmUnsecureVirtualMemory(hSecure); ZwFreeVirtualMemory( NtCurrentProcess(), &pv, &cj, MEM_RELEASE); pv = NULL; } } else { ZwFreeVirtualMemory( NtCurrentProcess(), &pv, &cj, MEM_RELEASE); pv = NULL; } } return(pv); } /******************************Public*Routize******************************\ * EngSecureMem() * * History: * 02-Oct-1995 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ HANDLE APIENTRY EngSecureMem( PVOID Address, ULONG Length ) { return (MmSecureVirtualMemory(Address, Length, PAGE_READWRITE)); } /******************************Public*Routine******************************\ * EngUnsecureMem() * * History: * 02-Oct-1995 -by- Andre Vachon [andreva] * Wrote it. * * Note: Forwarder only - no code needed \**************************************************************************/ /******************************Public*Routine******************************\ * EngFreeUserMem() * * History: * 10-Sep-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/ VOID EngFreeUserMem( PVOID pv ) { if (pv) { // Lookup the allocation in the Process's GDIEngUserMemAllocTable PW32PROCESS pW32Process = W32GetCurrentProcess(); GDIENGUSERMEMALLOCNODE lookupNode; PGDIENGUSERMEMALLOCNODE pFoundNode; lookupNode.pUserMem = pv; SIMPLELOCK sl(&pW32Process->GDIEngUserMemAllocTableLock); pFoundNode = (PGDIENGUSERMEMALLOCNODE) RtlLookupElementGenericTableAvl(&pW32Process->GDIEngUserMemAllocTable, &lookupNode); // If found, Unsecure it, free it and remove entry from // GDIEngUserMemAllocTable if (pFoundNode) { ULONG_PTR cj = (ULONG_PTR) pFoundNode->cj; HANDLE hSecure = pFoundNode->hSecure; MmUnsecureVirtualMemory(hSecure); ZwFreeVirtualMemory( NtCurrentProcess(), &pv, &cj, MEM_RELEASE); lookupNode.pUserMem = pv; RtlDeleteElementGenericTableAvl(&pW32Process->GDIEngUserMemAllocTable, &lookupNode); } } return; } /******************************Public*Routine******************************\ * EngDeviceIoControl() * * History: * 04-Feb-1995 -by- Andre Vachon [andreva] * Wrote it. \**************************************************************************/ NTSTATUS GreDeviceIoControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned ) { NTSTATUS Status = STATUS_NOT_IMPLEMENTED; IO_STATUS_BLOCK Iosb; PIRP pIrp; KEVENT event; if (hDevice == NULL) { return STATUS_INVALID_HANDLE; } if ( (nInBufferSize >= (PAGE_SIZE * 10000) ) || (nOutBufferSize >= (PAGE_SIZE * 10000)) || ((nInBufferSize + nOutBufferSize) >= (PAGE_SIZE * 10000))) { WARNING("EngDeviceIoControl is asked to allocate >= 10000 pages"); return (STATUS_INVALID_PARAMETER); } KeInitializeEvent(&event, SynchronizationEvent, FALSE); pIrp = IoBuildDeviceIoControlRequest( dwIoControlCode, (PDEVICE_OBJECT) hDevice, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, FALSE, &event, &Iosb); if (pIrp) { /* * Even though the remote video channel emulates a video port, this * code doesn't actually use a HANDLE it just uses a DEVICE_OBJECT. * Unfortueately, there's only one of those for the remote video driver * and so it's not Multi-Session enabled. Remember the file * object handle and stuff it in the call here. (This code could * be called from any process, but globals are in Session space.) */ PIO_STACK_LOCATION irpSp; if ( gProtocolType != PROTOCOL_CONSOLE ) { irpSp = IoGetNextIrpStackLocation( pIrp ); irpSp->FileObject = G_RemoteVideoFileObject; } Status = IoCallDriver((PDEVICE_OBJECT) hDevice, pIrp); // // If the call is synchronous, the IO is always completed // and the Status is the same as the Iosb.Status. // if (Status == STATUS_PENDING) { Status = KeWaitForSingleObject(&event, UserRequest, KernelMode, TRUE, NULL); Status = Iosb.Status; } *lpBytesReturned = (DWORD)Iosb.Information; } return (Status); } DWORD EngDeviceIoControl( HANDLE hDevice, DWORD dwIoControlCode, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesReturned ) { DWORD retStatus; NTSTATUS Status = GreDeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned); // // Do the inverse translation to what the video port does // so that we can have the original win32 status codes. // // Maybe, somehow, we can completely eliminate this double // translation - but I don't care for now. It's just a bit // longer on the very odd failiure case // switch (Status) { case STATUS_SUCCESS: retStatus = NO_ERROR; break; case STATUS_NOT_IMPLEMENTED: retStatus = ERROR_INVALID_FUNCTION; break; case STATUS_INSUFFICIENT_RESOURCES: retStatus = ERROR_NOT_ENOUGH_MEMORY; break; case STATUS_INVALID_PARAMETER: retStatus = ERROR_INVALID_PARAMETER; break; case STATUS_BUFFER_TOO_SMALL: retStatus = ERROR_INSUFFICIENT_BUFFER; break; case STATUS_BUFFER_OVERFLOW: retStatus = ERROR_MORE_DATA; break; case STATUS_PENDING: retStatus = ERROR_IO_PENDING; break; case STATUS_DEVICE_DOES_NOT_EXIST: retStatus = ERROR_DEV_NOT_EXIST; break; default: retStatus = Status; break; } return retStatus; } VOID EngMultiByteToUnicodeN( PWSTR UnicodeString, ULONG MaxBytesInUnicodeString, PULONG BytesInUnicodeString, PCHAR MultiByteString, ULONG BytesInMultiByteString ) { RtlMultiByteToUnicodeN(UnicodeString, MaxBytesInUnicodeString, BytesInUnicodeString, MultiByteString, BytesInMultiByteString); } VOID EngUnicodeToMultiByteN( PCHAR MultiByteString, ULONG MaxBytesInMultiByteString, PULONG BytesInMultiByteString, PWSTR UnicodeString, ULONG BytesInUnicodeString ) { RtlUnicodeToMultiByteN( MultiByteString, MaxBytesInMultiByteString, BytesInMultiByteString, UnicodeString, BytesInUnicodeString ); } /******************************Public*Routine******************************\ * EngQueryPerformanceCounter * * Queries the performance counter. * * It would have been preferable to use 'KeQueryTickCount,' but has a * resolution of about 10ms on an x86, which is not sufficient for * getting an accurate measure of the time between vertical blanks, which * is typically between 8ms and 17ms. * * NOTE: Use this routine sparingly, calling it as infrequently as possible! * Calling this routine too frequently can degrade I/O performance * for the calling driver and for the system as a whole. * * History: * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ VOID APIENTRY EngQueryPerformanceCounter( LONGLONG *pPerformanceCount ) { LARGE_INTEGER li; li = KeQueryPerformanceCounter(NULL); *pPerformanceCount = *((LONGLONG*) &li); } /******************************Public*Routine******************************\ * EngQueryPerformanceFrequency * * Queries the resolution of the performance counter. * * History: * 3-Dec-1995 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ VOID APIENTRY EngQueryPerformanceFrequency( LONGLONG *pFrequency ) { KeQueryPerformanceCounter((LARGE_INTEGER*) pFrequency); } /******************************Public*Routine******************************\ * EngAllocSectionMem * * Allocate mapped memory in session space. * * History: * 23-Oct-2000 -by- Chenyin Zhong [chenyz] * Wrote it. \**************************************************************************/ PVOID EngAllocSectionMem( PVOID *pSectionObject, ULONG fl, ULONG cj, ULONG tag ) { LARGE_INTEGER SectionSize; PVOID pMappedBase; NTSTATUS Status; HANDLE hSecure; SIZE_T MemSize = 0; // // Don't trust the driver to only ask for non-zero length buffers. // if (cj == 0) return(NULL); SectionSize.LowPart = cj; SectionSize.HighPart = 0; Status = Win32CreateSection( pSectionObject, SECTION_ALL_ACCESS, (POBJECT_ATTRIBUTES)NULL, &SectionSize, PAGE_READWRITE, SEC_COMMIT, (HANDLE)NULL, NULL, tag); if (!NT_SUCCESS(Status)) { KdPrint(("MmCreateSection fails in EngAllocSectionMem with error code: 0x%x\n", Status)); return(NULL); } cj = 0; pMappedBase = NULL; Status = Win32MapViewInSessionSpace(*pSectionObject, &pMappedBase, &MemSize); if (!NT_SUCCESS(Status)) { KdPrint(("MmMapViewInSessionSpace fails in EngAllocSectionMem with error code: 0x%x\n", Status)); Win32DestroySection(*pSectionObject); pMappedBase = NULL; *pSectionObject = NULL; } cj = (ULONG)MemSize; if (fl & FL_ZERO_MEMORY) { if (pMappedBase) { RtlZeroMemory(pMappedBase,cj); } } return pMappedBase; } /******************************Public*Routine******************************\ * EngFreeSectionMem() * * History: * 25-Oct-2000 -by- Chenyin Zhong [chenyz] * Wrote it. * 24-Jul-2002 -by- Ivan Leichtling [ivanlei] * Added return value \**************************************************************************/ BOOL EngFreeSectionMem( PVOID SectionObject, PVOID pv ) { NTSTATUS Status; BOOL bRet = TRUE; if(pv) { Status = Win32UnmapViewInSessionSpace(pv); if (!NT_SUCCESS(Status)) { KdPrint(("MmUnmapViewInSessionSpace fails in EngFreeSectionMem with error code: 0x%x\n", Status)); RIP("MmUnmapViewInSessionSpace failed!"); bRet = FALSE; } } if(SectionObject) { Win32DestroySection(SectionObject); } return bRet; } /******************************Public*Routine******************************\ * EngMapSection() * * History: * 25-Oct-2000 -by- Chenyin Zhong [chenyz] * Wrote it. \**************************************************************************/ BOOL EngMapSection( PVOID SectionObject, BOOL bMap, HANDLE ProcessHandle, PVOID *pMapBase ) { NTSTATUS Status; PEPROCESS Process; LARGE_INTEGER SectionOffset; ULONG AllocationType = 0L; SIZE_T ViewSize = 0; SectionOffset.LowPart = 0; SectionOffset.HighPart = 0; Status = ObReferenceObjectByHandle(ProcessHandle, PROCESS_VM_OPERATION, NULL, KernelMode, (PVOID *)&Process, NULL); if(!NT_SUCCESS(Status)) { return FALSE; } if(bMap) //Map section memory { *pMapBase = NULL; Status = MmMapViewOfSection(SectionObject, // SectionToMap, Process, // process pMapBase, // CapturedBase, 0L, // ZeroBits, 0L, // CommitSize, &SectionOffset, // SectionOffset, &ViewSize, // CapturedViewSize, ViewShare, // InheritDisposition, AllocationType, // AllocationType, PAGE_READWRITE); // Allow writing on this view if(!NT_SUCCESS(Status)) { ObDereferenceObject(Process); *pMapBase = NULL; KdPrint(("MmMapViewofSection fails in EngMapSection with error code: %u\n", Status)); return FALSE; } } else //Unmap section memory { Status = MmUnmapViewOfSection (Process, *pMapBase); if (!NT_SUCCESS(Status)) { ObDereferenceObject(Process); return FALSE; } } ObDereferenceObject (Process); return TRUE; } /******************************Public*Routine******************************\ * EngSave/RestoreFloatingPointState * * Saves the floating point state so that drivers can do floating point * operations. If the state were no preserved and floating point operations * were used, we would be corrupting the thread's user-mode state! * * History: * 17-Oct-1996 -by- J. Andrew Goossen [andrewgo] * Wrote it. \**************************************************************************/ typedef struct { BOOL bSaved; KFLOATING_SAVE fsFpState; } FP_STATE_SAVE; ULONG APIENTRY EngSaveFloatingPointState( VOID *pBuffer, ULONG cjBufferSize // Must be zero initialized ) { ULONG ulRet = 0; FP_STATE_SAVE* pFpStateSave; KFLOATING_SAVE fsTestState; pFpStateSave = (FP_STATE_SAVE*) pBuffer; if ((pFpStateSave == NULL) || (cjBufferSize == 0)) { // Check to see if the processor supports floating point by // simply seeing whether or not KeSaveFloatingPointState // succeeds: if (!NT_SUCCESS(KeSaveFloatingPointState(&fsTestState))) { // No floating point hardware. return(ulRet); } KeRestoreFloatingPointState(&fsTestState); return(sizeof(FP_STATE_SAVE)); } if (cjBufferSize < sizeof(FP_STATE_SAVE)) { KdPrint(("EngSaveFloatingPointState: The driver's buffer is too small.\n")); KdPrint(("Floating point corruption in the application may result.")); RIP("The driver must be fixed!"); return(ulRet); } if (pFpStateSave->bSaved) { KdPrint(("EngSaveFloatingPointState: Your driver called save twice in a row.\n")); KdPrint(("(Either that or it didn't zero initialize the buffer.)\n")); KdPrint(("Floating point corruption in the application may result.")); RIP("The driver must be fixed!"); // No point in returning failure because at this point they're just // plain hosed if they called Save twice in succession. If however // they simply did not zero initialize the buffer, things would still // be okay, because KeSaveFloatingPointState itself doesn't need the // buffer to be zero initialized. } ulRet = NT_SUCCESS(KeSaveFloatingPointState(&pFpStateSave->fsFpState)); pFpStateSave->bSaved = (ulRet != 0); return(ulRet); } BOOL APIENTRY EngRestoreFloatingPointState( VOID *pBuffer ) { FP_STATE_SAVE* pFpStateSave; pFpStateSave = (FP_STATE_SAVE*) pBuffer; if (!pFpStateSave->bSaved) { KdPrint(("EngRestoreFloatingPointState: Your driver called restore " "twice in a row or called restore without a preceeding " "successful call to save.\n")); KdPrint(("Floating point corruption in the application may result.")); RIP("The driver must be fixed!"); return(FALSE); } pFpStateSave->bSaved = FALSE; return(NT_SUCCESS(KeRestoreFloatingPointState(&pFpStateSave->fsFpState))); } /******************************Public*Routine******************************\ * * EngQuerySystemAttribute * * Can be used by a driver to query certain processor or system specific * capabilities. * \**************************************************************************/ BOOL EngQuerySystemAttribute( ENG_SYSTEM_ATTRIBUTE CapNum, PDWORD pCapability) { switch (CapNum) { case EngProcessorFeature: { SYSTEM_PROCESSOR_INFORMATION spi; if (NT_SUCCESS(ZwQuerySystemInformation( SystemProcessorInformation, &spi, sizeof(SYSTEM_PROCESSOR_INFORMATION), NULL))) { *pCapability = spi.ProcessorFeatureBits; return TRUE; } break; } case EngNumberOfProcessors: { SYSTEM_BASIC_INFORMATION sbi; if (NT_SUCCESS(ZwQuerySystemInformation( SystemBasicInformation, &sbi, sizeof(SYSTEM_BASIC_INFORMATION), NULL))) { *pCapability = sbi.NumberOfProcessors; return TRUE; } break; } case EngOptimumAvailableUserMemory: case EngOptimumAvailableSystemMemory: break; default: break; } return FALSE; } #if defined(_GDIPLUS_) VOID GreQuerySystemTime( PLARGE_INTEGER CurrentTime ) { GetSystemTimeAsFileTime((PFILETIME) CurrentTime); } VOID GreSystemTimeToLocalTime ( PLARGE_INTEGER SystemTime, PLARGE_INTEGER LocalTime ) { FileTimeToLocalFileTime((PFILETIME) SystemTime, (PFILETIME) LocalTime); } #else // !_GDIPLUS_ VOID GreQuerySystemTime( PLARGE_INTEGER CurrentTime ) { KeQuerySystemTime(CurrentTime); } VOID GreSystemTimeToLocalTime ( PLARGE_INTEGER SystemTime, PLARGE_INTEGER LocalTime ) { ExSystemTimeToLocalTime (SystemTime, LocalTime); } #endif // _GDIPLUS_ #ifdef DDI_WATCHDOG /******************************Public*Routine******************************\ * * GreCreateWatchdogs * * Creates an array of WATCHDOG_DATA objects pool, and creates and * initializes associated DPC and DEFERRED_WATCHDOG objects. * * Return Value: * * A pointer the new array or NULL * * Note: * * dpcCallback must be in non-pagable, non-session kernel memory. * DPC objects must be in non-paged, non-session kernel memory. * Watchdog objects must be in non-paged, non-session kernel memory. * Deferred context must be in non-paged, non-session kernel memory. * \**************************************************************************/ PWATCHDOG_DATA GreCreateWatchdogs( PDEVICE_OBJECT pDeviceObject, ULONG ulNumberOfWatchdogs, LONG lPeriod, PKDEFERRED_ROUTINE dpcCallback, PWSTR pwszDriverName, HANDLE hDriver, PLDEV *ppldevDriverList ) { PWATCHDOG_DATA pWatchdogData; if (NULL == pwszDriverName) { return NULL; } pWatchdogData = (PWATCHDOG_DATA)GdiAllocPool(ulNumberOfWatchdogs * sizeof (WATCHDOG_DATA), GDITAG_WATCHDOG); if (pWatchdogData) { PWD_GDI_DPC_CONTEXT pvContext; SIZE_T stSize; ULONG ulLength; ULONG i; ulLength = wcslen(pwszDriverName); stSize = sizeof(WD_GDI_DPC_CONTEXT) + ((ulLength + 1) * sizeof(WCHAR)); // // Zero out in case we won't be able to create all we need and we'll have to back off. // RtlZeroMemory(pWatchdogData, ulNumberOfWatchdogs * sizeof (WATCHDOG_DATA)); for (i = 0; i < ulNumberOfWatchdogs; i++) { // // Allocate DPC object from non-paged, non-session pool. // pWatchdogData[i].pDpc = (PKDPC)GdiAllocPoolNonPagedNS(sizeof (KDPC), GDITAG_WATCHDOG); if (NULL == pWatchdogData[i].pDpc) { // // Allocation failed - delete what we created so far and bail out. // GreDeleteWatchdogs(pWatchdogData, i); pWatchdogData = NULL; break; } // // Allocate deferred watchdog object. // pWatchdogData[i].pWatchdog = WdAllocateDeferredWatchdog(pDeviceObject, WdKernelTime, GDITAG_WATCHDOG); if (NULL == pWatchdogData[i].pWatchdog) { // // Allocation failed - delete what we created so far and bail out. // Note: We have to free last DPC object here. // GdiFreePool(pWatchdogData[i].pDpc); pWatchdogData[i].pDpc = NULL; GreDeleteWatchdogs(pWatchdogData, i); pWatchdogData = NULL; break; } // // Attach and initialize watchdog context. // pvContext = (PWD_GDI_DPC_CONTEXT)WdAttachContext(pWatchdogData[i].pWatchdog, (ULONG)stSize); if (pvContext) { pvContext->ppldevDrivers = ppldevDriverList; pvContext->hDriver = hDriver; // // Stuff UNICODE_STRING and driver name into the context attached to watchdog object. // RtlCopyMemory(pvContext+1, pwszDriverName, (ulLength + 1) * sizeof(WCHAR)); RtlInitUnicodeString(&pvContext->DisplayDriverName, (PCWSTR)(pvContext+1)); } else { // // Context creation failed - delete what we created so far and bail out. // GreDeleteWatchdogs(pWatchdogData, i + 1); pWatchdogData = NULL; break; } // // Initialize DPC object and start deferred watch. // KeInitializeDpc(pWatchdogData[i].pDpc, dpcCallback, pvContext); WdStartDeferredWatch(pWatchdogData[i].pWatchdog, pWatchdogData[i].pDpc, lPeriod); } } return pWatchdogData; } /******************************Public*Routine******************************\ * * GreDeleteWatchdogs * * Stops watchdogs and deletes an array of WATCHDOG_DATA objects. * \**************************************************************************/ VOID GreDeleteWatchdogs( PWATCHDOG_DATA pWatchdogData, ULONG ulNumberOfWatchdogs ) { if (NULL != pWatchdogData) { ULONG i; for (i = 0; i < ulNumberOfWatchdogs; i++) { if (NULL != pWatchdogData[i].pWatchdog) { // // Stop and free deferred watchdog. // WdStopDeferredWatch(pWatchdogData[i].pWatchdog); WdFreeDeferredWatchdog(pWatchdogData[i].pWatchdog); pWatchdogData[i].pWatchdog = NULL; } if (NULL != pWatchdogData[i].pDpc) { // // Free pool allocated for DPC object. // GdiFreePool(pWatchdogData[i].pDpc); pWatchdogData[i].pDpc = NULL; } } // // Free pool allocated for WatchdogData array. // GdiFreePool(pWatchdogData); } } #endif // DDI_WATCHDOG