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