/*++ Copyright (c) 2001 Microsoft Corporation Module Name: utils.c Abstract: This file contains utility code for the RAM disk driver. Author: Chuck Lenzmeier (ChuckL) 2001 Environment: Kernel mode only. Notes: Revision History: --*/ #include "precomp.h" #pragma hdrstop #ifdef ALLOC_PRAGMA #if defined(POOL_DBG) #pragma alloc_text( INIT, RamdiskInitializePoolDebug ) #endif // POOL_DBG #endif // ALLOC_PRAGMA NTSTATUS SendIrpToThread ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine sends an IRP off to the worker thread so that it can be processed in thread context. Arguments: DeviceObject - a pointer to the object that represents the device on which I/O is to be performed Irp - a pointer to the I/O Request Packet for this request Return Value: None. --*/ { PIO_WORKITEM workItem; // // Mark the IRP pending. Queue the IRP to a worker thread. // IoMarkIrpPending( Irp ); workItem = IoAllocateWorkItem( DeviceObject ); if ( workItem != NULL ) { // // Save the work item pointer so the worker thread can find it. // Irp->Tail.Overlay.DriverContext[0] = workItem; IoQueueWorkItem( workItem, RamdiskWorkerThread, DelayedWorkQueue, Irp ); return STATUS_PENDING; } return STATUS_INSUFFICIENT_RESOURCES; } // SendIrpToThread PUCHAR RamdiskMapPages ( IN PDISK_EXTENSION DiskExtension, IN ULONGLONG Offset, IN ULONG RequestedLength, OUT PULONG ActualLength ) /*++ Routine Description: This routine maps pages of a RAM disk image into the system process. Arguments: DiskExtension - a pointer to the device extension for the target device object Offset - the offset into the RAM disk image at which the mapping is to start RequestedLength - the desired length of the mapping ActualLength - returns the actual length of the mapping. This will be less than or equal to RequestedLength. If less than, the caller will need to call again to get the remainder of the desired range mapped. Because the number of available ranges may be limited, the caller should execute the required operation on one segment of the range and unmap it before mapping the next segment. Return Value: PUCHAR - a pointer to the mapped space; NULL if the mapping failed --*/ { NTSTATUS status; PUCHAR va; ULONGLONG diskRelativeOffset; ULONGLONG fileRelativeOffset; ULONG viewRelativeOffset; DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: offset %I64x, length %x\n", Offset, RequestedLength) ); // // The input Offset is relative to the start of the disk image, which // may not be the same as the start of the file or memory block. Capture // Offset into diskRelativeOffset, then calculate fileRelativeOffset as // the offset from the start of the file or memory block. // diskRelativeOffset = Offset; fileRelativeOffset = DiskExtension->DiskOffset + diskRelativeOffset; if ( RAMDISK_IS_FILE_BACKED(DiskExtension->DiskType) ) { // // For a file-backed RAM disk, we need to map the range into memory. // while ( TRUE ) { PLIST_ENTRY listEntry; PVIEW view; // // Lock the list of view descriptors. // KeEnterCriticalRegion(); ExAcquireFastMutex( &DiskExtension->Mutex ); // // Walk the list of view descriptors. Look for one that includes the // start of the range we're mapping. // DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: looking for matching view; file offset %I64x\n", fileRelativeOffset) ); listEntry = DiskExtension->ViewsByOffset.Flink; while ( listEntry != &DiskExtension->ViewsByOffset ) { view = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry ); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: view %p; offset %I64x, length %x\n", view, view->Offset, view->Length) ); ASSERT( (view->Offset + view->Length) >= view->Offset ); if ( (view->Offset <= fileRelativeOffset) && (view->Offset + view->Length) > fileRelativeOffset ) { // // This view includes the start of our range. Reference it. // DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: choosing existing view %p; offset %I64x, length %x\n", view, view->Offset, view->Length) ); if ( !view->Permanent ) { view->ReferenceCount++; DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: view %p; new refcount %x\n", view, view->ReferenceCount) ); } else { DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: view %p is permanent\n", view) ); } // // Move the view to the front of the MRU list. // RemoveEntryList( &view->ByMruListEntry ); InsertHeadList( &DiskExtension->ViewsByMru, &view->ByMruListEntry ); ExReleaseFastMutex( &DiskExtension->Mutex ); KeLeaveCriticalRegion(); // // Calculate the amount of data that the caller can look // at in this range. Usually this will be the requested // amount, but if the caller's offset is close to the end // of a view, the caller will only be able to look at data // up to the end of the view. // viewRelativeOffset = (ULONG)(fileRelativeOffset - view->Offset); *ActualLength = view->Length - viewRelativeOffset; if ( *ActualLength > RequestedLength ) { *ActualLength = RequestedLength; } DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: requested length %x; mapped length %x\n", RequestedLength, *ActualLength) ); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: view base %p; returned VA %p\n", view->Address, view->Address + viewRelativeOffset) ); // // Return the virtual address corresponding to the caller's // specified offset, which will usually be offset from the // base of the view. // return view->Address + viewRelativeOffset; } // // This view does not include the start of our range. If the view // starts above the start of our range, then our range is not // currently mapped. // if ( view->Offset > fileRelativeOffset ) { DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("%s", "RamdiskMapPages: view too high; our range not mapped\n") ); break; } // // Check the next view in the list. // listEntry = listEntry->Flink; } // // We didn't find a view that maps the start of our range. Look for a // free view descriptor. // DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("%s", "RamdiskMapPages: looking for free view\n") ); listEntry = DiskExtension->ViewsByMru.Blink; while ( listEntry != &DiskExtension->ViewsByMru ) { view = CONTAINING_RECORD( listEntry, VIEW, ByMruListEntry ); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: view %p; permanent %x, refcount %x\n", view, view->Permanent, view->ReferenceCount) ); if ( !view->Permanent && (view->ReferenceCount == 0) ) { // // This view descriptor is free. If it's currently mapped, // unmap it. // PVOID mappedAddress; ULONGLONG mappedOffset; SIZE_T mappedLength; DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: view %p is free\n", view) ); if ( view->Address != NULL ) { DBGPRINT( DBG_WINDOW, DBG_VERBOSE, ("RamdiskMapPages: unmapping view %p; offset %I64x, " "length %x, addr %p\n", view, view->Offset, view->Length, view->Address) ); MmUnmapViewOfSection( PsGetCurrentProcess(), view->Address ); // // Reset the view descriptor and move it to the tail of // the MRU list and the head of the by-offset list. We // do this here in case we have to bail later (because // mapping a new view fails). // view->Offset = 0; view->Length = 0; view->Address = NULL; RemoveEntryList( listEntry ); InsertTailList( &DiskExtension->ViewsByMru, listEntry ); RemoveEntryList( &view->ByOffsetListEntry ); InsertHeadList( &DiskExtension->ViewsByOffset, &view->ByOffsetListEntry ); } // // Map a view to include the start of our range. Round the // caller's offset down to the start of a view range. // mappedOffset = fileRelativeOffset & ~(ULONGLONG)(DiskExtension->ViewLength - 1); mappedLength = DiskExtension->ViewLength; if ( (mappedOffset + mappedLength) > DiskExtension->FileRelativeEndOfDisk) { mappedLength = (SIZE_T)(DiskExtension->FileRelativeEndOfDisk - mappedOffset); } mappedAddress = NULL; DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: remapping view %p; offset %I64x, " "length %x\n", view, mappedOffset, mappedLength) ); status = MmMapViewOfSection( DiskExtension->SectionObject, PsGetCurrentProcess(), &mappedAddress, 0, 0, (PLARGE_INTEGER)&mappedOffset, &mappedLength, ViewUnmap, 0, PAGE_READWRITE ); if ( !NT_SUCCESS(status) ) { // // Unable to map the range. Inform the caller by returning // NULL. // // ISSUE: Think about unmapping another region to see if // mapping will then succeed. // DBGPRINT( DBG_WINDOW, DBG_ERROR, ("RamdiskMapPages: unable to map view: %x\n", status) ); ExReleaseFastMutex( &DiskExtension->Mutex ); KeLeaveCriticalRegion(); return NULL; } DBGPRINT( DBG_WINDOW, DBG_VERBOSE, ("RamdiskMapPages: remapped view %p; offset %I64x, " "length %x, addr %p\n", view, mappedOffset, mappedLength, mappedAddress) ); // // Capture the mapped range information into the view // descriptor. Set the reference count to 1. Insert the // view at the front of the MRU list, and at the // appropriate point in the by-offset list. // view->Offset = mappedOffset; view->Length = (ULONG)mappedLength; view->Address = mappedAddress; ASSERT( (view->Offset + view->Length) >= view->Offset ); view->ReferenceCount = 1; RemoveEntryList( &view->ByMruListEntry ); InsertHeadList( &DiskExtension->ViewsByMru, &view->ByMruListEntry ); // // Remove the view descriptor from its current point in // the by-offset list (at or near the front, because it's // currently unmapped). Scan from the tail of the by-offset // list (highest offset down), looking for the first view // that has an offset less than or equal to the new view. // Insert the new view after that view. (If there are no // views with an offset <= this one, it goes at the front // of the list.) // RemoveEntryList( &view->ByOffsetListEntry ); listEntry = DiskExtension->ViewsByOffset.Blink; while ( listEntry != &DiskExtension->ViewsByOffset ) { PVIEW view2 = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry ); if ( view2->Offset <= view->Offset ) { break; } listEntry = listEntry->Blink; } InsertHeadList( listEntry, &view->ByOffsetListEntry ); ExReleaseFastMutex( &DiskExtension->Mutex ); KeLeaveCriticalRegion(); // // Calculate the amount of data that the caller can look // at in this range. Usually this will be the requested // amount, but if the caller's offset is close to the end // of a view, the caller will only be able to look at data // up to the end of the view. // viewRelativeOffset = (ULONG)(fileRelativeOffset - view->Offset); *ActualLength = view->Length - viewRelativeOffset; if ( *ActualLength > RequestedLength ) { *ActualLength = RequestedLength; } DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: requested length %x; mapped length %x\n", RequestedLength, *ActualLength) ); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: view base %p; returned VA %p\n", view->Address, view->Address + viewRelativeOffset) ); // // Return the virtual address corresponding to the caller's // specified offset, which will usually be offset from the // base of the view. // return view->Address + viewRelativeOffset; } // // This view is not free. Try the previous view in the MRU list. // listEntry = listEntry->Blink; } // // We were unable to find a free view descriptor. Wait for one to // become available and start over. // // Before leaving the critical section, increment the count of // waiters. Then leave the critical section and wait on the // semaphore. The unmap code uses the waiter count to determine // how many times to release the semaphore. In this way, all // threads that are waiting or have decided to wait when the // unmap code runs will be awakened. // DiskExtension->ViewWaiterCount++; DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskMapPages: can't find free view, so waiting; new waiter count %x\n", DiskExtension->ViewWaiterCount) ); ExReleaseFastMutex( &DiskExtension->Mutex ); KeLeaveCriticalRegion(); status = KeWaitForSingleObject( &DiskExtension->ViewSemaphore, Executive, KernelMode, FALSE, NULL ); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("%s", "RamdiskMapPages: done waiting for free view\n") ); } } else if ( DiskExtension->DiskType == RAMDISK_TYPE_BOOT_DISK ) { // // For a boot disk RAM disk, the image is contained in contiguous // reserved physical pages. Use MmMapIoSpace to get a virtual // address that corresponds to the physical address. // ULONG mappingSize; PHYSICAL_ADDRESS physicalAddress; PUCHAR mappedAddress; // // Determine how many pages must be mapped. Determine the base // physical address of the desired range. Map the range. // mappingSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(fileRelativeOffset, RequestedLength) * PAGE_SIZE; physicalAddress.QuadPart = (DiskExtension->BasePage + (fileRelativeOffset / PAGE_SIZE)) * PAGE_SIZE; mappedAddress = MmMapIoSpace( physicalAddress, mappingSize, MmCached ); if ( mappedAddress == NULL ) { // // Unable to map the physical pages. Return NULL. // va = NULL; } else { // // Add the offset in the page to the returned virtual address. // va = mappedAddress + (fileRelativeOffset & (PAGE_SIZE - 1)); } *ActualLength = RequestedLength; } else { // // For a virtual floppy RAM disk, the image is contained in contiguous // virtual memory. // ASSERT( DiskExtension->DiskType == RAMDISK_TYPE_VIRTUAL_FLOPPY ); va = (PUCHAR)DiskExtension->BaseAddress + fileRelativeOffset; *ActualLength = RequestedLength; } return va; } // RamdiskMapPages VOID RamdiskUnmapPages ( IN PDISK_EXTENSION DiskExtension, IN PUCHAR Va, IN ULONGLONG Offset, IN ULONG Length ) /*++ Routine Description: This routine unmaps previously mapped pages of a RAM disk image. Arguments: DiskExtension - a pointer to the device extension for the target device object Va - the virtual address assigned to the mapping. This is unused for file-backed RAM disks. Offset - the offset into the RAM disk image at which the mapping starts Length - the length of the mapping Return Value: None. --*/ { ULONGLONG diskRelativeOffset; ULONGLONG fileRelativeOffset; ULONG viewRelativeOffset; // // The input Offset is relative to the start of the disk image, which // may not be the same as the start of the file or memory block. Capture // Offset into diskRelativeOffset, then calculate fileRelativeOffset as // the offset from the start of the file or memory block. // diskRelativeOffset = Offset; fileRelativeOffset = DiskExtension->DiskOffset + diskRelativeOffset; if ( RAMDISK_IS_FILE_BACKED(DiskExtension->DiskType) ) { // // For a file-backed RAM disk, we need to decrement the reference // count on all views that cover the specified range. // // Note: In the current implementation, no caller ever maps more // than one range at a time, and therefore no call to this routine // will need to dereference more than one view. But this routine // is written to allow for ranges that cover multiple views. // PLIST_ENTRY listEntry; PVIEW view; ULONGLONG rangeStart = fileRelativeOffset; ULONGLONG rangeEnd = fileRelativeOffset + Length; BOOLEAN wakeWaiters = FALSE; // // Lock the list of view descriptors. // KeEnterCriticalRegion(); ExAcquireFastMutex( &DiskExtension->Mutex ); // // Walk the list of view descriptors. For each one that includes the // range that we're unmapping, decrement the reference count. // listEntry = DiskExtension->ViewsByOffset.Flink; while ( Length != 0 ) { ASSERT( listEntry != &DiskExtension->ViewsByOffset ); view = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry ); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskUnmapPages: view %p; offset %I64x, length %x\n", view, view->Offset, view->Length) ); if ( (view->Offset + view->Length) <= rangeStart ) { // // This view lies entirely below our range. Move on. // DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("%s", "RamdiskMapPages: view too low; skipping\n") ); listEntry = listEntry->Flink; ASSERT( listEntry != &DiskExtension->ViewsByOffset ); continue; } // // This view does not lie below our range. Since the view list // is ordered by offset, and we have length left to unmap, this // view must NOT lie entirely ABOVE our range. // ASSERT( view->Offset < rangeEnd ); // // Decrement the reference count for this view. If the count goes // to zero, we need to inform any waiters that at least one free // view is available. // // ISSUE: Note that unreferenced views remain mapped indefinitely. // We only unmap a view when we need to map a different view. If // a RAM disk goes idle, its views remain mapped, using up virtual // address space in the system process. With the current default // view count and length, this is 8 MB of VA. This is probably // not enough to make it worthwhile to implement a timer to unmap // idle views. // DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskUnmapPages: dereferencing view %p; offset %I64x, length %x\n", view, view->Offset, view->Length) ); if ( !view->Permanent ) { view->ReferenceCount--; if ( view->ReferenceCount == 0 ) { wakeWaiters = TRUE; } DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskUnmapPages: view %p; new refcount %x\n", view, view->ReferenceCount) ); } else { DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskUnmapPages: view %p is permanent\n", view) ); } // // Subtract the length of this view from the amount we're // unmapping. If the view fully encompasses our range, we're done. // if ( (view->Offset + view->Length) >= rangeEnd ) { Length = 0; } else { viewRelativeOffset = (ULONG)(fileRelativeOffset - view->Offset); Length -= view->Length - viewRelativeOffset; Offset = view->Offset + view->Length; ASSERT( Length != 0 ); // // Move to the next view. // listEntry = listEntry->Flink; } } // // If one or more views are now free, and there are threads waiting, // wake them up now. // if ( wakeWaiters && (DiskExtension->ViewWaiterCount != 0) ) { DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskUnmapPages: waking %x waiters\n", DiskExtension->ViewWaiterCount) ); KeReleaseSemaphore( &DiskExtension->ViewSemaphore, 0, DiskExtension->ViewWaiterCount, FALSE ); DiskExtension->ViewWaiterCount = 0; } ExReleaseFastMutex( &DiskExtension->Mutex ); KeLeaveCriticalRegion(); } else if ( DiskExtension->DiskType == RAMDISK_TYPE_BOOT_DISK ) { // // For a boot disk RAM disk, use MmUnmapIoSpace to undo what // RamdiskMapPages did. // PUCHAR mappedAddress; ULONG mappingSize; // // The actual mapped address is at the base of the page given by Va. // The actual length of the mapping is based on the number of pages // covered by the range specified by Offset and Length. // mappedAddress = Va - (fileRelativeOffset & (PAGE_SIZE - 1)); mappingSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(fileRelativeOffset, Length) * PAGE_SIZE; MmUnmapIoSpace( mappedAddress, mappingSize ); } return; } // RamdiskUnmapPages NTSTATUS RamdiskFlushViews ( IN PDISK_EXTENSION DiskExtension ) { NTSTATUS status; NTSTATUS returnStatus; IO_STATUS_BLOCK iosb; PLIST_ENTRY listEntry; PVIEW view; SIZE_T viewLength; PAGED_CODE(); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("%s", "RamdiskFlushViews\n") ); ASSERT( RAMDISK_IS_FILE_BACKED(DiskExtension->DiskType) ); // // Lock the list of view descriptors. // // // Walk the list of view descriptors. For each one that is currently // mapped, flush its virtual memory to the backing file. // returnStatus = STATUS_SUCCESS; KeEnterCriticalRegion(); ExAcquireFastMutex( &DiskExtension->Mutex ); listEntry = DiskExtension->ViewsByOffset.Flink; while ( listEntry != &DiskExtension->ViewsByOffset ) { view = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry ); DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("RamdiskFlushViews: view %p; addr %p, offset %I64x, length %x\n", view, view->Address, view->Offset, view->Length) ); if ( view->Address != NULL ) { // // This view is mapped. Flush it. // DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("%s", "RamdiskMapPages: view mapped; flushing\n") ); viewLength = view->Length; status = ZwFlushVirtualMemory( NtCurrentProcess(), &view->Address, &viewLength, &iosb ); if ( NT_SUCCESS(status) ) { status = iosb.Status; } if ( !NT_SUCCESS(status) ) { DBGPRINT( DBG_WINDOW, DBG_ERROR, ("RamdiskFlushViews: ZwFlushVirtualMemory failed: %x\n", status) ); if ( returnStatus == STATUS_SUCCESS ) { returnStatus = status; } } } // // Move to the next view. // listEntry = listEntry->Flink; } ExReleaseFastMutex( &DiskExtension->Mutex ); KeLeaveCriticalRegion(); return returnStatus; } // RamdiskFlushViews // // Pool allocation debugging code. // #if defined(POOL_DBG) // // Allocations owned by the driver (both allocated by and deallocated by the // driver) have the following header. // typedef struct _MY_POOL { union { CHAR Signature[8]; ULONG SigLong[2]; } ; LIST_ENTRY ListEntry; PVOID File; ULONG Line; POOL_TYPE Type; } MY_POOL, *PMY_POOL; #define MY_SIGNATURE "RaMdIsK" LIST_ENTRY RamdiskNonpagedPoolList; LIST_ENTRY RamdiskPagedPoolList; FAST_MUTEX RamdiskPoolMutex; KSPIN_LOCK RamdiskPoolSpinLock; VOID RamdiskInitializePoolDebug ( VOID ) { InitializeListHead( &RamdiskNonpagedPoolList ); InitializeListHead( &RamdiskPagedPoolList ); ExInitializeFastMutex( &RamdiskPoolMutex ); KeInitializeSpinLock( &RamdiskPoolSpinLock ); return; } // RamdiskInitializePoolDebug PVOID RamdiskAllocatePoolWithTag ( POOL_TYPE PoolType, SIZE_T Size, ULONG Tag, LOGICAL Private, PCHAR File, ULONG Line ) { PMY_POOL myPool; KIRQL oldIrql; HRESULT result; if ( !Private ) { // // This is not a private allocation (it will be deallocated by some // other piece of code). We can't put a header on it. // myPool = ExAllocatePoolWithTag( PoolType, Size, Tag ); DBGPRINT( DBG_POOL, DBG_PAINFUL, ("Allocated %d bytes at %p for %s/%d\n", Size, myPool + 1, File, Line) ); return myPool; } // // Allocate the requested space plus room for our header. // myPool = ExAllocatePoolWithTag( PoolType, sizeof(MY_POOL) + Size, Tag ); if ( myPool == NULL ) { return NULL; } // // Fill in the header. // result = StringCbCopyA( myPool->Signature, sizeof( myPool->Signature ), MY_SIGNATURE ); ASSERT( result == S_OK ); myPool->File = File; myPool->Line = Line; myPool->Type = PoolType; // // Link the block into the appropriate list. If nonpaged pool, we must use // a spin lock to guard the list, because deallocation might happen at // raised IRQL. The paged pool list can be guarded by a mutex. // // NB: BASE_POOL_TYPE_MASK is defined in ntos\inc\pool.h. // #define BASE_POOL_TYPE_MASK 1 if ( (PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool ) { KeAcquireSpinLock( &RamdiskPoolSpinLock, &oldIrql ); InsertTailList( &RamdiskNonpagedPoolList, &myPool->ListEntry ); KeReleaseSpinLock( &RamdiskPoolSpinLock, oldIrql ); } else { KeEnterCriticalRegion(); ExAcquireFastMutex( &RamdiskPoolMutex ); InsertTailList( &RamdiskPagedPoolList, &myPool->ListEntry ); ExReleaseFastMutex( &RamdiskPoolMutex ); KeLeaveCriticalRegion(); } // // Return a pointer to the caller's area, not to our header. // DBGPRINT( DBG_POOL, DBG_PAINFUL, ("Allocated %d bytes at %p for %s/%d\n", Size, myPool + 1, File, Line) ); return myPool + 1; } // RamdiskAllocatePoolWithTag VOID RamdiskFreePool ( PVOID Address, LOGICAL Private, PCHAR File, ULONG Line ) { PMY_POOL myPool; PLIST_ENTRY list; PLIST_ENTRY listEntry; LOGICAL found; KIRQL oldIrql; // // The following line is here to get PREfast to stop complaining about the // call to KeReleaseSpinLock using an uninitialized variable. // oldIrql = 0; DBGPRINT( DBG_POOL, DBG_PAINFUL, ("Freeing pool at %p for %s/%d\n", Address, File, Line) ); if ( !Private ) { // // This is not a private allocation (it was allocated by some other // piece of code). It doesn't have our header. // ExFreePool( Address ); return; } // // Get the address of our header. Check that the header has our signature. // myPool = (PMY_POOL)Address - 1; if ( strcmp( myPool->Signature, MY_SIGNATURE ) != 0 ) { DbgPrint( "%s", "RAMDISK: Attempt to free pool block not owned by ramdisk.sys!!!\n" ); DbgPrint( " address: %p, freeing file: %s, line: %d\n", Address, File, Line ); ASSERT( FALSE ); // // Since it doesn't look like our header, assume that it wasn't // really a private allocation. // ExFreePool( Address ); return; } // // Remove the block from the allocation list. First, acquire the // appropriate lock. // if ( (myPool->Type & BASE_POOL_TYPE_MASK) == NonPagedPool ) { list = &RamdiskNonpagedPoolList; KeAcquireSpinLock( &RamdiskPoolSpinLock, &oldIrql ); } else { list = &RamdiskPagedPoolList; KeEnterCriticalRegion(); ExAcquireFastMutex( &RamdiskPoolMutex ); } // // Search the list for this block. // found = FALSE; for ( listEntry = list->Flink; listEntry != list; listEntry = listEntry->Flink ) { if ( listEntry == &myPool->ListEntry ) { // // Found this block. Remove it from the list and leave the loop. // RemoveEntryList( listEntry ); found = TRUE; break; } } // // Release the lock. // if ( (myPool->Type & BASE_POOL_TYPE_MASK) == NonPagedPool ) { KeReleaseSpinLock( &RamdiskPoolSpinLock, oldIrql ); } else { ExReleaseFastMutex( &RamdiskPoolMutex ); KeLeaveCriticalRegion(); } if ( !found ) { // // Didn't find the block in the list. Complain. // DbgPrint( "%s", "RAMDISK: Attempt to free pool block not in allocation list!!!\n" ); DbgPrint( " address: %p, freeing file: %s, line: %d\n", myPool, File, Line ); DbgPrint( " allocating file: %s, line: %d\n", myPool->File, myPool->Line ); ASSERT( FALSE ); } // // Free the pool block. // ExFreePool( myPool ); return; } // RamdiskFreePool #endif // defined(POOL_DBG)