422 lines
11 KiB
C
422 lines
11 KiB
C
|
#include "wdmsys.h"
|
||
|
|
||
|
#pragma LOCKED_CODE
|
||
|
#pragma LOCKED_DATA
|
||
|
|
||
|
|
||
|
//
|
||
|
// Robust checking enables bugchecks in the retail version of this component.
|
||
|
//
|
||
|
BOOL gbRobustChecking = FALSE;
|
||
|
|
||
|
#if 0
|
||
|
//
|
||
|
// PagedCode: This routine should be used in routines that touch pageable
|
||
|
// code and data. Notice that this is not the PAGED_CODE() macro so that it
|
||
|
// will be in the retail code.
|
||
|
//
|
||
|
void
|
||
|
PagedCode(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
if( KeGetCurrentIrql() > APC_LEVEL )
|
||
|
{
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_NOT_BELOW_DISPATCH_LEVEL,0,0,0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
ValidatePassiveLevel(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
if( KeGetCurrentIrql() != PASSIVE_LEVEL )
|
||
|
{
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_NOT_AT_PASSIVE_LEVEL,0,0,0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If bExclusive is TRUE, the caller wants to know if the mutex is acquired multiple
|
||
|
// times on the same thread. If they don't want this checking done, they should
|
||
|
// set bExclusive to FALSE.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
AudioEnterMutex(
|
||
|
IN PKMUTEX pmutex,
|
||
|
IN BOOL bExclusive
|
||
|
)
|
||
|
{
|
||
|
PRKTHREAD pkt;
|
||
|
NTSTATUS Status;
|
||
|
LONG lMutexState;
|
||
|
|
||
|
Status = KeWaitForSingleObject ( pmutex,Executive,KernelMode,FALSE,NULL ) ;
|
||
|
if( gbRobustChecking && bExclusive )
|
||
|
{
|
||
|
//
|
||
|
// After entering the mutex, we sanity check our nested count, but we
|
||
|
// only do this if we need to. If the caller said that they designed the
|
||
|
// code only for exclusive access, we'll bugcheck if we get nested.
|
||
|
//
|
||
|
if( (lMutexState = KeReadStateMutex(pmutex)) < 0 )
|
||
|
{
|
||
|
//
|
||
|
// Every time you acquire a mutex the state will be decremented. 1 means
|
||
|
// that it's available, 0 means it's held, -1 means this is the second time
|
||
|
// it's acquired, -2 means it's the third and so on. Thus, if we're nested
|
||
|
// on this thread, we'll end up here.
|
||
|
//
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,
|
||
|
AUDIO_NESTED_MUTEX_SITUATION,
|
||
|
(ULONG_PTR)pmutex,
|
||
|
lMutexState,
|
||
|
0);
|
||
|
}
|
||
|
}
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This routine yields if we're at PASSIVE_LEVEL
|
||
|
//
|
||
|
void
|
||
|
AudioPerformYield(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
PRKTHREAD pthrd;
|
||
|
KPRIORITY kpriority;
|
||
|
|
||
|
//
|
||
|
// After releasing the mutex we want to yield execution to all other threads
|
||
|
// on the machine to try to expose preemption windows.
|
||
|
//
|
||
|
if( ( gbRobustChecking ) &&
|
||
|
( KeGetCurrentIrql() == PASSIVE_LEVEL ) )
|
||
|
{
|
||
|
if( (pthrd = KeGetCurrentThread()) )
|
||
|
{
|
||
|
kpriority = KeQueryPriorityThread(KeGetCurrentThread());
|
||
|
//
|
||
|
// Lower this thrread's priority so another thread will
|
||
|
// get scheduled.
|
||
|
//
|
||
|
KeSetPriorityThread(pthrd,1); // Is that low enough?
|
||
|
|
||
|
//
|
||
|
// This might be overkill, but yield.
|
||
|
//
|
||
|
ZwYieldExecution();
|
||
|
|
||
|
//
|
||
|
// Now restore the priority for this thread and party on.
|
||
|
//
|
||
|
KeSetPriorityThread(pthrd,kpriority);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
AudioLeaveMutex(
|
||
|
IN PKMUTEX pmutex
|
||
|
)
|
||
|
{
|
||
|
PRKTHREAD pthrd;
|
||
|
KPRIORITY kpriority;
|
||
|
|
||
|
KeReleaseMutex ( pmutex, FALSE ) ;
|
||
|
|
||
|
AudioPerformYield();
|
||
|
}
|
||
|
|
||
|
NTSTATUS
|
||
|
AudioIoCallDriver (
|
||
|
IN PDEVICE_OBJECT pDevice,
|
||
|
IN PIRP pIrp
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
Status = IoCallDriver(pDevice,pIrp);
|
||
|
|
||
|
AudioPerformYield();
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
AudioEnterSpinLock(
|
||
|
IN PKSPIN_LOCK pSpinLock,
|
||
|
OUT PKIRQL pOldIrql
|
||
|
)
|
||
|
{
|
||
|
//
|
||
|
// KeAcquireSpinLock can only be called less then or equal to dispatch level.
|
||
|
// let's verify that here.
|
||
|
//
|
||
|
if( ( gbRobustChecking ) &&
|
||
|
( KeGetCurrentIrql() > DISPATCH_LEVEL ) )
|
||
|
{
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,
|
||
|
AUDIO_INVALID_IRQL_LEVEL,
|
||
|
0,
|
||
|
0,
|
||
|
0);
|
||
|
}
|
||
|
|
||
|
KeAcquireSpinLock ( pSpinLock, pOldIrql ) ;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
AudioLeaveSpinLock(
|
||
|
IN PKSPIN_LOCK pSpinLock,
|
||
|
IN KIRQL OldIrql
|
||
|
)
|
||
|
{
|
||
|
KeReleaseSpinLock ( pSpinLock, OldIrql ) ;
|
||
|
AudioPerformYield();
|
||
|
}
|
||
|
|
||
|
|
||
|
void
|
||
|
AudioObDereferenceObject(
|
||
|
IN PVOID pvObject
|
||
|
)
|
||
|
{
|
||
|
ObDereferenceObject(pvObject);
|
||
|
AudioPerformYield();
|
||
|
}
|
||
|
|
||
|
void
|
||
|
AudioIoCompleteRequest(
|
||
|
IN PIRP pIrp,
|
||
|
IN CCHAR PriorityBoost
|
||
|
)
|
||
|
{
|
||
|
IoCompleteRequest(pIrp,PriorityBoost);
|
||
|
|
||
|
AudioPerformYield();
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#define MEMORY_LIMIT_CHECK 262144
|
||
|
|
||
|
//
|
||
|
// This routine assumes that pptr points to a memory location that currently
|
||
|
// contains a NULL value. Thus, on failure, this routine does not have to
|
||
|
// write back a NULL value.
|
||
|
//
|
||
|
// Entry:
|
||
|
// if dwFlags contains:
|
||
|
//#define DEFAULT_MEMORY 0x00 // Standard ExAllocatePool call
|
||
|
//#define ZERO_FILL_MEMORY 0x01 // Zero the memory
|
||
|
//#define QUOTA_MEMORY 0x02 // ExAllocatePoolWithQuota call
|
||
|
//#define LIMIT_MEMORY 0x04 // Never allocation more then 1/4 Meg
|
||
|
//#define FIXED_MEMORY 0x08 // Use locked memory
|
||
|
//#define PAGED_MEMORY 0x10 // use pageable memory
|
||
|
//
|
||
|
NTSTATUS
|
||
|
AudioAllocateMemory(
|
||
|
IN SIZE_T bytes,
|
||
|
IN ULONG tag,
|
||
|
IN AAMFLAGS dwFlags,
|
||
|
OUT PVOID *pptr
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
POOL_TYPE pooltype;
|
||
|
PVOID pInit;
|
||
|
KIRQL irql;
|
||
|
PVOID ptr = NULL;
|
||
|
|
||
|
ASSERT(*pptr == NULL);
|
||
|
|
||
|
if( 0 == bytes )
|
||
|
{
|
||
|
//
|
||
|
// The code should never be asking for zero bytes. The core will bugcheck
|
||
|
// on a call like this. In debug mode we'll assert. In retail, we'll
|
||
|
// return an error.
|
||
|
//
|
||
|
ASSERT(0);
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
} else {
|
||
|
|
||
|
if( dwFlags & FIXED_MEMORY )
|
||
|
pooltype = NonPagedPool;
|
||
|
else
|
||
|
pooltype = PagedPool;
|
||
|
|
||
|
//
|
||
|
// Allocating pageable memory at DISPATCH_LEVEL is a bad thing to do.
|
||
|
// here we make sure that is not the case.
|
||
|
//
|
||
|
if( ( (irql = KeGetCurrentIrql()) > DISPATCH_LEVEL ) ||
|
||
|
((DISPATCH_LEVEL == irql ) && (NonPagedPool != pooltype)) )
|
||
|
{
|
||
|
//
|
||
|
// Either way, we've got an error, bugcheck or exit.
|
||
|
//
|
||
|
if( gbRobustChecking )
|
||
|
{
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_INVALID_IRQL_LEVEL,0,0,0);
|
||
|
} else {
|
||
|
//
|
||
|
// If we can't bugcheck, then we're going to return an error
|
||
|
// in this case.
|
||
|
//
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto exit;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Let's see if the caller has asked to limit the allocation to a "reasonable"
|
||
|
// number of bytes. If so, "reasonable" is 1/4 meg.
|
||
|
//
|
||
|
if( ( dwFlags & LIMIT_MEMORY ) && ( bytes > MEMORY_LIMIT_CHECK ) )
|
||
|
{
|
||
|
//
|
||
|
// For some reason, this allocation is trying to allocate more then was designed.
|
||
|
// Why is the caller allocating so much?
|
||
|
//
|
||
|
if( gbRobustChecking )
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_ABSURD_ALLOCATION_ATTEMPTED,0,0,0);
|
||
|
|
||
|
//
|
||
|
// Checked build will assert if robust checking is not enabled.
|
||
|
//
|
||
|
ASSERT("Memory Allocation Unreasonable!");
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate the memory, NULL is returned on failure in the normal case, an exception
|
||
|
// is raised with quota. ptr is NULLed out above for the Quota routine.
|
||
|
//
|
||
|
if( dwFlags & QUOTA_MEMORY )
|
||
|
{
|
||
|
//
|
||
|
// Caller wants to charge the allocation to the current user context.
|
||
|
//
|
||
|
try
|
||
|
{
|
||
|
|
||
|
ptr = ExAllocatePoolWithQuotaTag(pooltype,bytes,tag);
|
||
|
|
||
|
}except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
} else {
|
||
|
//
|
||
|
// Don't want to assign this memory to the quota.
|
||
|
//
|
||
|
ptr = ExAllocatePoolWithTag(pooltype, bytes, tag);
|
||
|
}
|
||
|
|
||
|
if( ptr )
|
||
|
{
|
||
|
if( dwFlags & ZERO_FILL_MEMORY )
|
||
|
{
|
||
|
RtlFillMemory( ptr,bytes,0 );
|
||
|
}
|
||
|
//
|
||
|
// Never allocate over the top of an existing pointer. Interlock exchange
|
||
|
// with the location. This interlock updates the location in the caller.
|
||
|
//
|
||
|
if( pInit = InterlockedExchangePointer(pptr,ptr) )
|
||
|
{
|
||
|
//
|
||
|
// If we get a return value from this exchange it means that there
|
||
|
// was already a pointer in the location that we added a pointer too.
|
||
|
// If this is the case, most likely we overwrote a valid memory
|
||
|
// pointer. With robust checking we don't want this to happen.
|
||
|
//
|
||
|
if( gbRobustChecking )
|
||
|
{
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,
|
||
|
AUDIO_MEMORY_ALLOCATION_OVERWRITE,
|
||
|
(ULONG_PTR)pInit,
|
||
|
0,0);
|
||
|
}
|
||
|
//
|
||
|
// If we end up here, we've overwritten a memory pointer with a
|
||
|
// successful memory allocation. Or ASSERT at the top of this
|
||
|
// function should have fired. In any case, we can return success
|
||
|
// and how that nothing bad happens.
|
||
|
//
|
||
|
}
|
||
|
Status = STATUS_SUCCESS;
|
||
|
} else {
|
||
|
//
|
||
|
// Our memory allocation failed. *pptr should still be NULL
|
||
|
// return error.
|
||
|
//
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
}
|
||
|
}
|
||
|
exit:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// This routine is safe to call with a NULL memory pointer. It will return
|
||
|
// STATUS_UNSUCCESSFUL and do nothing if passed a NULL pointer. Also, this routine
|
||
|
// reads the location atomically so it should be safe in a multiproc environment.
|
||
|
//
|
||
|
void
|
||
|
AudioFreeMemory(
|
||
|
IN SIZE_T bytes,
|
||
|
IN OUT PVOID *pptr
|
||
|
)
|
||
|
{
|
||
|
PVOID pFree;
|
||
|
KIRQL irql;
|
||
|
|
||
|
//
|
||
|
// Interlockedexhchange the pointer will NULL, validate that it's non-NULL
|
||
|
// trash the memory if needed and then free the pointer.
|
||
|
//
|
||
|
pFree = InterlockedExchangePointer(pptr,NULL);
|
||
|
if( pFree )
|
||
|
{
|
||
|
//
|
||
|
// We have a pointer to free.
|
||
|
//
|
||
|
if( gbRobustChecking )
|
||
|
{
|
||
|
//
|
||
|
// The docs say that we need to be careful how we call the free routine
|
||
|
// with regard to IRQlevel. Thus, if we're being robust we'll do this
|
||
|
// extra work.
|
||
|
//
|
||
|
if( (irql = KeGetCurrentIrql()) > DISPATCH_LEVEL )
|
||
|
{
|
||
|
KeBugCheckEx(AUDIO_BUGCHECK_CODE,AUDIO_INVALID_IRQL_LEVEL,0,0,0);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Under debug we're going to put 'k's in the memory location in order
|
||
|
// to make extra sure that no one is using this memory.
|
||
|
//
|
||
|
if( UNKNOWN_SIZE != bytes )
|
||
|
{
|
||
|
RtlFillMemory( pFree,bytes,'k' );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now we actually free the memory.
|
||
|
//
|
||
|
ExFreePool(pFree);
|
||
|
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|