Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2840 lines
82 KiB

/******************************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