Source code of Windows XP (NT5)
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.

1151 lines
34 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. utils.c
  5. Abstract:
  6. This file contains utility code for the RAM disk driver.
  7. Author:
  8. Chuck Lenzmeier (ChuckL) 2001
  9. Environment:
  10. Kernel mode only.
  11. Notes:
  12. Revision History:
  13. --*/
  14. #include "precomp.h"
  15. #pragma hdrstop
  16. #ifdef ALLOC_PRAGMA
  17. #if defined(POOL_DBG)
  18. #pragma alloc_text( INIT, RamdiskInitializePoolDebug )
  19. #endif // POOL_DBG
  20. #endif // ALLOC_PRAGMA
  21. NTSTATUS
  22. SendIrpToThread (
  23. IN PDEVICE_OBJECT DeviceObject,
  24. IN PIRP Irp
  25. )
  26. /*++
  27. Routine Description:
  28. This routine sends an IRP off to the worker thread so that it can be
  29. processed in thread context.
  30. Arguments:
  31. DeviceObject - a pointer to the object that represents the device on which
  32. I/O is to be performed
  33. Irp - a pointer to the I/O Request Packet for this request
  34. Return Value:
  35. None.
  36. --*/
  37. {
  38. PIO_WORKITEM workItem;
  39. //
  40. // Mark the IRP pending. Queue the IRP to a worker thread.
  41. //
  42. IoMarkIrpPending( Irp );
  43. workItem = IoAllocateWorkItem( DeviceObject );
  44. if ( workItem != NULL ) {
  45. //
  46. // Save the work item pointer so the worker thread can find it.
  47. //
  48. Irp->Tail.Overlay.DriverContext[0] = workItem;
  49. IoQueueWorkItem( workItem, RamdiskWorkerThread, DelayedWorkQueue, Irp );
  50. return STATUS_PENDING;
  51. }
  52. return STATUS_INSUFFICIENT_RESOURCES;
  53. } // SendIrpToThread
  54. PUCHAR
  55. RamdiskMapPages (
  56. IN PDISK_EXTENSION DiskExtension,
  57. IN ULONGLONG Offset,
  58. IN ULONG RequestedLength,
  59. OUT PULONG ActualLength
  60. )
  61. /*++
  62. Routine Description:
  63. This routine maps pages of a RAM disk image into the system process.
  64. Arguments:
  65. DiskExtension - a pointer to the device extension for the target device
  66. object
  67. Offset - the offset into the RAM disk image at which the mapping is to
  68. start
  69. RequestedLength - the desired length of the mapping
  70. ActualLength - returns the actual length of the mapping. This will be less
  71. than or equal to RequestedLength. If less than, the caller will need
  72. to call again to get the remainder of the desired range mapped.
  73. Because the number of available ranges may be limited, the caller
  74. should execute the required operation on one segment of the range and
  75. unmap it before mapping the next segment.
  76. Return Value:
  77. PUCHAR - a pointer to the mapped space; NULL if the mapping failed
  78. --*/
  79. {
  80. NTSTATUS status;
  81. PUCHAR va;
  82. ULONGLONG diskRelativeOffset;
  83. ULONGLONG fileRelativeOffset;
  84. ULONG viewRelativeOffset;
  85. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  86. ("RamdiskMapPages: offset %I64x, length %x\n", Offset, RequestedLength) );
  87. //
  88. // The input Offset is relative to the start of the disk image, which
  89. // may not be the same as the start of the file or memory block. Capture
  90. // Offset into diskRelativeOffset, then calculate fileRelativeOffset as
  91. // the offset from the start of the file or memory block.
  92. //
  93. diskRelativeOffset = Offset;
  94. fileRelativeOffset = DiskExtension->DiskOffset + diskRelativeOffset;
  95. if ( RAMDISK_IS_FILE_BACKED(DiskExtension->DiskType) ) {
  96. //
  97. // For a file-backed RAM disk, we need to map the range into memory.
  98. //
  99. while ( TRUE ) {
  100. PLIST_ENTRY listEntry;
  101. PVIEW view;
  102. //
  103. // Lock the list of view descriptors.
  104. //
  105. KeEnterCriticalRegion();
  106. ExAcquireFastMutex( &DiskExtension->Mutex );
  107. //
  108. // Walk the list of view descriptors. Look for one that includes the
  109. // start of the range we're mapping.
  110. //
  111. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  112. ("RamdiskMapPages: looking for matching view; file offset %I64x\n",
  113. fileRelativeOffset) );
  114. listEntry = DiskExtension->ViewsByOffset.Flink;
  115. while ( listEntry != &DiskExtension->ViewsByOffset ) {
  116. view = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry );
  117. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  118. ("RamdiskMapPages: view %p; offset %I64x, length %x\n",
  119. view, view->Offset, view->Length) );
  120. ASSERT( (view->Offset + view->Length) >= view->Offset );
  121. if ( (view->Offset <= fileRelativeOffset) &&
  122. (view->Offset + view->Length) > fileRelativeOffset ) {
  123. //
  124. // This view includes the start of our range. Reference it.
  125. //
  126. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  127. ("RamdiskMapPages: choosing existing view %p; offset %I64x, length %x\n",
  128. view, view->Offset, view->Length) );
  129. if ( !view->Permanent ) {
  130. view->ReferenceCount++;
  131. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  132. ("RamdiskMapPages: view %p; new refcount %x\n",
  133. view, view->ReferenceCount) );
  134. } else {
  135. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  136. ("RamdiskMapPages: view %p is permanent\n", view) );
  137. }
  138. //
  139. // Move the view to the front of the MRU list.
  140. //
  141. RemoveEntryList( &view->ByMruListEntry );
  142. InsertHeadList( &DiskExtension->ViewsByMru, &view->ByMruListEntry );
  143. ExReleaseFastMutex( &DiskExtension->Mutex );
  144. KeLeaveCriticalRegion();
  145. //
  146. // Calculate the amount of data that the caller can look
  147. // at in this range. Usually this will be the requested
  148. // amount, but if the caller's offset is close to the end
  149. // of a view, the caller will only be able to look at data
  150. // up to the end of the view.
  151. //
  152. viewRelativeOffset = (ULONG)(fileRelativeOffset - view->Offset);
  153. *ActualLength = view->Length - viewRelativeOffset;
  154. if ( *ActualLength > RequestedLength ) {
  155. *ActualLength = RequestedLength;
  156. }
  157. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  158. ("RamdiskMapPages: requested length %x; mapped length %x\n",
  159. RequestedLength, *ActualLength) );
  160. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  161. ("RamdiskMapPages: view base %p; returned VA %p\n",
  162. view->Address,
  163. view->Address + viewRelativeOffset) );
  164. //
  165. // Return the virtual address corresponding to the caller's
  166. // specified offset, which will usually be offset from the
  167. // base of the view.
  168. //
  169. return view->Address + viewRelativeOffset;
  170. }
  171. //
  172. // This view does not include the start of our range. If the view
  173. // starts above the start of our range, then our range is not
  174. // currently mapped.
  175. //
  176. if ( view->Offset > fileRelativeOffset ) {
  177. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  178. ("%s", "RamdiskMapPages: view too high; our range not mapped\n") );
  179. break;
  180. }
  181. //
  182. // Check the next view in the list.
  183. //
  184. listEntry = listEntry->Flink;
  185. }
  186. //
  187. // We didn't find a view that maps the start of our range. Look for a
  188. // free view descriptor.
  189. //
  190. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  191. ("%s", "RamdiskMapPages: looking for free view\n") );
  192. listEntry = DiskExtension->ViewsByMru.Blink;
  193. while ( listEntry != &DiskExtension->ViewsByMru ) {
  194. view = CONTAINING_RECORD( listEntry, VIEW, ByMruListEntry );
  195. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  196. ("RamdiskMapPages: view %p; permanent %x, refcount %x\n",
  197. view, view->Permanent, view->ReferenceCount) );
  198. if ( !view->Permanent && (view->ReferenceCount == 0) ) {
  199. //
  200. // This view descriptor is free. If it's currently mapped,
  201. // unmap it.
  202. //
  203. PVOID mappedAddress;
  204. ULONGLONG mappedOffset;
  205. SIZE_T mappedLength;
  206. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  207. ("RamdiskMapPages: view %p is free\n", view) );
  208. if ( view->Address != NULL ) {
  209. DBGPRINT( DBG_WINDOW, DBG_VERBOSE,
  210. ("RamdiskMapPages: unmapping view %p; offset %I64x, "
  211. "length %x, addr %p\n", view, view->Offset,
  212. view->Length, view->Address) );
  213. MmUnmapViewOfSection( PsGetCurrentProcess(), view->Address );
  214. //
  215. // Reset the view descriptor and move it to the tail of
  216. // the MRU list and the head of the by-offset list. We
  217. // do this here in case we have to bail later (because
  218. // mapping a new view fails).
  219. //
  220. view->Offset = 0;
  221. view->Length = 0;
  222. view->Address = NULL;
  223. RemoveEntryList( listEntry );
  224. InsertTailList( &DiskExtension->ViewsByMru, listEntry );
  225. RemoveEntryList( &view->ByOffsetListEntry );
  226. InsertHeadList( &DiskExtension->ViewsByOffset, &view->ByOffsetListEntry );
  227. }
  228. //
  229. // Map a view to include the start of our range. Round the
  230. // caller's offset down to the start of a view range.
  231. //
  232. mappedOffset = fileRelativeOffset & ~(ULONGLONG)(DiskExtension->ViewLength - 1);
  233. mappedLength = DiskExtension->ViewLength;
  234. if ( (mappedOffset + mappedLength) > DiskExtension->FileRelativeEndOfDisk) {
  235. mappedLength = (SIZE_T)(DiskExtension->FileRelativeEndOfDisk - mappedOffset);
  236. }
  237. mappedAddress = NULL;
  238. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  239. ("RamdiskMapPages: remapping view %p; offset %I64x, "
  240. "length %x\n", view, mappedOffset, mappedLength) );
  241. status = MmMapViewOfSection(
  242. DiskExtension->SectionObject,
  243. PsGetCurrentProcess(),
  244. &mappedAddress,
  245. 0,
  246. 0,
  247. (PLARGE_INTEGER)&mappedOffset,
  248. &mappedLength,
  249. ViewUnmap,
  250. 0,
  251. PAGE_READWRITE
  252. );
  253. if ( !NT_SUCCESS(status) ) {
  254. //
  255. // Unable to map the range. Inform the caller by returning
  256. // NULL.
  257. //
  258. // ISSUE: Think about unmapping another region to see if
  259. // mapping will then succeed.
  260. //
  261. DBGPRINT( DBG_WINDOW, DBG_ERROR,
  262. ("RamdiskMapPages: unable to map view: %x\n", status) );
  263. ExReleaseFastMutex( &DiskExtension->Mutex );
  264. KeLeaveCriticalRegion();
  265. return NULL;
  266. }
  267. DBGPRINT( DBG_WINDOW, DBG_VERBOSE,
  268. ("RamdiskMapPages: remapped view %p; offset %I64x, "
  269. "length %x, addr %p\n", view, mappedOffset, mappedLength,
  270. mappedAddress) );
  271. //
  272. // Capture the mapped range information into the view
  273. // descriptor. Set the reference count to 1. Insert the
  274. // view at the front of the MRU list, and at the
  275. // appropriate point in the by-offset list.
  276. //
  277. view->Offset = mappedOffset;
  278. view->Length = (ULONG)mappedLength;
  279. view->Address = mappedAddress;
  280. ASSERT( (view->Offset + view->Length) >= view->Offset );
  281. view->ReferenceCount = 1;
  282. RemoveEntryList( &view->ByMruListEntry );
  283. InsertHeadList( &DiskExtension->ViewsByMru, &view->ByMruListEntry );
  284. //
  285. // Remove the view descriptor from its current point in
  286. // the by-offset list (at or near the front, because it's
  287. // currently unmapped). Scan from the tail of the by-offset
  288. // list (highest offset down), looking for the first view
  289. // that has an offset less than or equal to the new view.
  290. // Insert the new view after that view. (If there are no
  291. // views with an offset <= this one, it goes at the front
  292. // of the list.)
  293. //
  294. RemoveEntryList( &view->ByOffsetListEntry );
  295. listEntry = DiskExtension->ViewsByOffset.Blink;
  296. while ( listEntry != &DiskExtension->ViewsByOffset ) {
  297. PVIEW view2 = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry );
  298. if ( view2->Offset <= view->Offset ) {
  299. break;
  300. }
  301. listEntry = listEntry->Blink;
  302. }
  303. InsertHeadList( listEntry, &view->ByOffsetListEntry );
  304. ExReleaseFastMutex( &DiskExtension->Mutex );
  305. KeLeaveCriticalRegion();
  306. //
  307. // Calculate the amount of data that the caller can look
  308. // at in this range. Usually this will be the requested
  309. // amount, but if the caller's offset is close to the end
  310. // of a view, the caller will only be able to look at data
  311. // up to the end of the view.
  312. //
  313. viewRelativeOffset = (ULONG)(fileRelativeOffset - view->Offset);
  314. *ActualLength = view->Length - viewRelativeOffset;
  315. if ( *ActualLength > RequestedLength ) {
  316. *ActualLength = RequestedLength;
  317. }
  318. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  319. ("RamdiskMapPages: requested length %x; mapped length %x\n",
  320. RequestedLength, *ActualLength) );
  321. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  322. ("RamdiskMapPages: view base %p; returned VA %p\n",
  323. view->Address,
  324. view->Address + viewRelativeOffset) );
  325. //
  326. // Return the virtual address corresponding to the caller's
  327. // specified offset, which will usually be offset from the
  328. // base of the view.
  329. //
  330. return view->Address + viewRelativeOffset;
  331. }
  332. //
  333. // This view is not free. Try the previous view in the MRU list.
  334. //
  335. listEntry = listEntry->Blink;
  336. }
  337. //
  338. // We were unable to find a free view descriptor. Wait for one to
  339. // become available and start over.
  340. //
  341. // Before leaving the critical section, increment the count of
  342. // waiters. Then leave the critical section and wait on the
  343. // semaphore. The unmap code uses the waiter count to determine
  344. // how many times to release the semaphore. In this way, all
  345. // threads that are waiting or have decided to wait when the
  346. // unmap code runs will be awakened.
  347. //
  348. DiskExtension->ViewWaiterCount++;
  349. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  350. ("RamdiskMapPages: can't find free view, so waiting; new waiter count %x\n",
  351. DiskExtension->ViewWaiterCount) );
  352. ExReleaseFastMutex( &DiskExtension->Mutex );
  353. KeLeaveCriticalRegion();
  354. status = KeWaitForSingleObject(
  355. &DiskExtension->ViewSemaphore,
  356. Executive,
  357. KernelMode,
  358. FALSE,
  359. NULL );
  360. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  361. ("%s", "RamdiskMapPages: done waiting for free view\n") );
  362. }
  363. } else if ( DiskExtension->DiskType == RAMDISK_TYPE_BOOT_DISK ) {
  364. //
  365. // For a boot disk RAM disk, the image is contained in contiguous
  366. // reserved physical pages. Use MmMapIoSpace to get a virtual
  367. // address that corresponds to the physical address.
  368. //
  369. ULONG mappingSize;
  370. PHYSICAL_ADDRESS physicalAddress;
  371. PUCHAR mappedAddress;
  372. //
  373. // Determine how many pages must be mapped. Determine the base
  374. // physical address of the desired range. Map the range.
  375. //
  376. mappingSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(fileRelativeOffset, RequestedLength) * PAGE_SIZE;
  377. physicalAddress.QuadPart = (DiskExtension->BasePage +
  378. (fileRelativeOffset / PAGE_SIZE)) * PAGE_SIZE;
  379. mappedAddress = MmMapIoSpace( physicalAddress, mappingSize, MmCached );
  380. if ( mappedAddress == NULL ) {
  381. //
  382. // Unable to map the physical pages. Return NULL.
  383. //
  384. va = NULL;
  385. } else {
  386. //
  387. // Add the offset in the page to the returned virtual address.
  388. //
  389. va = mappedAddress + (fileRelativeOffset & (PAGE_SIZE - 1));
  390. }
  391. *ActualLength = RequestedLength;
  392. } else {
  393. //
  394. // For a virtual floppy RAM disk, the image is contained in contiguous
  395. // virtual memory.
  396. //
  397. ASSERT( DiskExtension->DiskType == RAMDISK_TYPE_VIRTUAL_FLOPPY );
  398. va = (PUCHAR)DiskExtension->BaseAddress + fileRelativeOffset;
  399. *ActualLength = RequestedLength;
  400. }
  401. return va;
  402. } // RamdiskMapPages
  403. VOID
  404. RamdiskUnmapPages (
  405. IN PDISK_EXTENSION DiskExtension,
  406. IN PUCHAR Va,
  407. IN ULONGLONG Offset,
  408. IN ULONG Length
  409. )
  410. /*++
  411. Routine Description:
  412. This routine unmaps previously mapped pages of a RAM disk image.
  413. Arguments:
  414. DiskExtension - a pointer to the device extension for the target device
  415. object
  416. Va - the virtual address assigned to the mapping. This is unused for
  417. file-backed RAM disks.
  418. Offset - the offset into the RAM disk image at which the mapping starts
  419. Length - the length of the mapping
  420. Return Value:
  421. None.
  422. --*/
  423. {
  424. ULONGLONG diskRelativeOffset;
  425. ULONGLONG fileRelativeOffset;
  426. ULONG viewRelativeOffset;
  427. //
  428. // The input Offset is relative to the start of the disk image, which
  429. // may not be the same as the start of the file or memory block. Capture
  430. // Offset into diskRelativeOffset, then calculate fileRelativeOffset as
  431. // the offset from the start of the file or memory block.
  432. //
  433. diskRelativeOffset = Offset;
  434. fileRelativeOffset = DiskExtension->DiskOffset + diskRelativeOffset;
  435. if ( RAMDISK_IS_FILE_BACKED(DiskExtension->DiskType) ) {
  436. //
  437. // For a file-backed RAM disk, we need to decrement the reference
  438. // count on all views that cover the specified range.
  439. //
  440. // Note: In the current implementation, no caller ever maps more
  441. // than one range at a time, and therefore no call to this routine
  442. // will need to dereference more than one view. But this routine
  443. // is written to allow for ranges that cover multiple views.
  444. //
  445. PLIST_ENTRY listEntry;
  446. PVIEW view;
  447. ULONGLONG rangeStart = fileRelativeOffset;
  448. ULONGLONG rangeEnd = fileRelativeOffset + Length;
  449. BOOLEAN wakeWaiters = FALSE;
  450. //
  451. // Lock the list of view descriptors.
  452. //
  453. KeEnterCriticalRegion();
  454. ExAcquireFastMutex( &DiskExtension->Mutex );
  455. //
  456. // Walk the list of view descriptors. For each one that includes the
  457. // range that we're unmapping, decrement the reference count.
  458. //
  459. listEntry = DiskExtension->ViewsByOffset.Flink;
  460. while ( Length != 0 ) {
  461. ASSERT( listEntry != &DiskExtension->ViewsByOffset );
  462. view = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry );
  463. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  464. ("RamdiskUnmapPages: view %p; offset %I64x, length %x\n",
  465. view, view->Offset, view->Length) );
  466. if ( (view->Offset + view->Length) <= rangeStart ) {
  467. //
  468. // This view lies entirely below our range. Move on.
  469. //
  470. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  471. ("%s", "RamdiskMapPages: view too low; skipping\n") );
  472. listEntry = listEntry->Flink;
  473. ASSERT( listEntry != &DiskExtension->ViewsByOffset );
  474. continue;
  475. }
  476. //
  477. // This view does not lie below our range. Since the view list
  478. // is ordered by offset, and we have length left to unmap, this
  479. // view must NOT lie entirely ABOVE our range.
  480. //
  481. ASSERT( view->Offset < rangeEnd );
  482. //
  483. // Decrement the reference count for this view. If the count goes
  484. // to zero, we need to inform any waiters that at least one free
  485. // view is available.
  486. //
  487. // ISSUE: Note that unreferenced views remain mapped indefinitely.
  488. // We only unmap a view when we need to map a different view. If
  489. // a RAM disk goes idle, its views remain mapped, using up virtual
  490. // address space in the system process. With the current default
  491. // view count and length, this is 8 MB of VA. This is probably
  492. // not enough to make it worthwhile to implement a timer to unmap
  493. // idle views.
  494. //
  495. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  496. ("RamdiskUnmapPages: dereferencing view %p; offset %I64x, length %x\n",
  497. view, view->Offset, view->Length) );
  498. if ( !view->Permanent ) {
  499. view->ReferenceCount--;
  500. if ( view->ReferenceCount == 0 ) {
  501. wakeWaiters = TRUE;
  502. }
  503. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  504. ("RamdiskUnmapPages: view %p; new refcount %x\n",
  505. view, view->ReferenceCount) );
  506. } else {
  507. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  508. ("RamdiskUnmapPages: view %p is permanent\n", view) );
  509. }
  510. //
  511. // Subtract the length of this view from the amount we're
  512. // unmapping. If the view fully encompasses our range, we're done.
  513. //
  514. if ( (view->Offset + view->Length) >= rangeEnd ) {
  515. Length = 0;
  516. } else {
  517. viewRelativeOffset = (ULONG)(fileRelativeOffset - view->Offset);
  518. Length -= view->Length - viewRelativeOffset;
  519. Offset = view->Offset + view->Length;
  520. ASSERT( Length != 0 );
  521. //
  522. // Move to the next view.
  523. //
  524. listEntry = listEntry->Flink;
  525. }
  526. }
  527. //
  528. // If one or more views are now free, and there are threads waiting,
  529. // wake them up now.
  530. //
  531. if ( wakeWaiters && (DiskExtension->ViewWaiterCount != 0) ) {
  532. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  533. ("RamdiskUnmapPages: waking %x waiters\n",
  534. DiskExtension->ViewWaiterCount) );
  535. KeReleaseSemaphore(
  536. &DiskExtension->ViewSemaphore,
  537. 0,
  538. DiskExtension->ViewWaiterCount,
  539. FALSE
  540. );
  541. DiskExtension->ViewWaiterCount = 0;
  542. }
  543. ExReleaseFastMutex( &DiskExtension->Mutex );
  544. KeLeaveCriticalRegion();
  545. } else if ( DiskExtension->DiskType == RAMDISK_TYPE_BOOT_DISK ) {
  546. //
  547. // For a boot disk RAM disk, use MmUnmapIoSpace to undo what
  548. // RamdiskMapPages did.
  549. //
  550. PUCHAR mappedAddress;
  551. ULONG mappingSize;
  552. //
  553. // The actual mapped address is at the base of the page given by Va.
  554. // The actual length of the mapping is based on the number of pages
  555. // covered by the range specified by Offset and Length.
  556. //
  557. mappedAddress = Va - (fileRelativeOffset & (PAGE_SIZE - 1));
  558. mappingSize = ADDRESS_AND_SIZE_TO_SPAN_PAGES(fileRelativeOffset, Length) * PAGE_SIZE;
  559. MmUnmapIoSpace( mappedAddress, mappingSize );
  560. }
  561. return;
  562. } // RamdiskUnmapPages
  563. NTSTATUS
  564. RamdiskFlushViews (
  565. IN PDISK_EXTENSION DiskExtension
  566. )
  567. {
  568. NTSTATUS status;
  569. NTSTATUS returnStatus;
  570. IO_STATUS_BLOCK iosb;
  571. PLIST_ENTRY listEntry;
  572. PVIEW view;
  573. SIZE_T viewLength;
  574. PAGED_CODE();
  575. DBGPRINT( DBG_WINDOW, DBG_PAINFUL, ("%s", "RamdiskFlushViews\n") );
  576. ASSERT( RAMDISK_IS_FILE_BACKED(DiskExtension->DiskType) );
  577. //
  578. // Lock the list of view descriptors.
  579. //
  580. //
  581. // Walk the list of view descriptors. For each one that is currently
  582. // mapped, flush its virtual memory to the backing file.
  583. //
  584. returnStatus = STATUS_SUCCESS;
  585. KeEnterCriticalRegion();
  586. ExAcquireFastMutex( &DiskExtension->Mutex );
  587. listEntry = DiskExtension->ViewsByOffset.Flink;
  588. while ( listEntry != &DiskExtension->ViewsByOffset ) {
  589. view = CONTAINING_RECORD( listEntry, VIEW, ByOffsetListEntry );
  590. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  591. ("RamdiskFlushViews: view %p; addr %p, offset %I64x, length %x\n",
  592. view, view->Address, view->Offset, view->Length) );
  593. if ( view->Address != NULL ) {
  594. //
  595. // This view is mapped. Flush it.
  596. //
  597. DBGPRINT( DBG_WINDOW, DBG_PAINFUL,
  598. ("%s", "RamdiskMapPages: view mapped; flushing\n") );
  599. viewLength = view->Length;
  600. status = ZwFlushVirtualMemory(
  601. NtCurrentProcess(),
  602. &view->Address,
  603. &viewLength,
  604. &iosb
  605. );
  606. if ( NT_SUCCESS(status) ) {
  607. status = iosb.Status;
  608. }
  609. if ( !NT_SUCCESS(status) ) {
  610. DBGPRINT( DBG_WINDOW, DBG_ERROR,
  611. ("RamdiskFlushViews: ZwFlushVirtualMemory failed: %x\n", status) );
  612. if ( returnStatus == STATUS_SUCCESS ) {
  613. returnStatus = status;
  614. }
  615. }
  616. }
  617. //
  618. // Move to the next view.
  619. //
  620. listEntry = listEntry->Flink;
  621. }
  622. ExReleaseFastMutex( &DiskExtension->Mutex );
  623. KeLeaveCriticalRegion();
  624. return returnStatus;
  625. } // RamdiskFlushViews
  626. //
  627. // Pool allocation debugging code.
  628. //
  629. #if defined(POOL_DBG)
  630. //
  631. // Allocations owned by the driver (both allocated by and deallocated by the
  632. // driver) have the following header.
  633. //
  634. typedef struct _MY_POOL {
  635. union {
  636. CHAR Signature[8];
  637. ULONG SigLong[2];
  638. } ;
  639. LIST_ENTRY ListEntry;
  640. PVOID File;
  641. ULONG Line;
  642. POOL_TYPE Type;
  643. } MY_POOL, *PMY_POOL;
  644. #define MY_SIGNATURE "RaMdIsK"
  645. LIST_ENTRY RamdiskNonpagedPoolList;
  646. LIST_ENTRY RamdiskPagedPoolList;
  647. FAST_MUTEX RamdiskPoolMutex;
  648. KSPIN_LOCK RamdiskPoolSpinLock;
  649. VOID
  650. RamdiskInitializePoolDebug (
  651. VOID
  652. )
  653. {
  654. InitializeListHead( &RamdiskNonpagedPoolList );
  655. InitializeListHead( &RamdiskPagedPoolList );
  656. ExInitializeFastMutex( &RamdiskPoolMutex );
  657. KeInitializeSpinLock( &RamdiskPoolSpinLock );
  658. return;
  659. } // RamdiskInitializePoolDebug
  660. PVOID
  661. RamdiskAllocatePoolWithTag (
  662. POOL_TYPE PoolType,
  663. SIZE_T Size,
  664. ULONG Tag,
  665. LOGICAL Private,
  666. PCHAR File,
  667. ULONG Line
  668. )
  669. {
  670. PMY_POOL myPool;
  671. KIRQL oldIrql;
  672. HRESULT result;
  673. if ( !Private ) {
  674. //
  675. // This is not a private allocation (it will be deallocated by some
  676. // other piece of code). We can't put a header on it.
  677. //
  678. myPool = ExAllocatePoolWithTag( PoolType, Size, Tag );
  679. DBGPRINT( DBG_POOL, DBG_PAINFUL,
  680. ("Allocated %d bytes at %p for %s/%d\n", Size, myPool + 1, File, Line) );
  681. return myPool;
  682. }
  683. //
  684. // Allocate the requested space plus room for our header.
  685. //
  686. myPool = ExAllocatePoolWithTag( PoolType, sizeof(MY_POOL) + Size, Tag );
  687. if ( myPool == NULL ) {
  688. return NULL;
  689. }
  690. //
  691. // Fill in the header.
  692. //
  693. result = StringCbCopyA( myPool->Signature, sizeof( myPool->Signature ), MY_SIGNATURE );
  694. ASSERT( result == S_OK );
  695. myPool->File = File;
  696. myPool->Line = Line;
  697. myPool->Type = PoolType;
  698. //
  699. // Link the block into the appropriate list. If nonpaged pool, we must use
  700. // a spin lock to guard the list, because deallocation might happen at
  701. // raised IRQL. The paged pool list can be guarded by a mutex.
  702. //
  703. // NB: BASE_POOL_TYPE_MASK is defined in ntos\inc\pool.h.
  704. //
  705. #define BASE_POOL_TYPE_MASK 1
  706. if ( (PoolType & BASE_POOL_TYPE_MASK) == NonPagedPool ) {
  707. KeAcquireSpinLock( &RamdiskPoolSpinLock, &oldIrql );
  708. InsertTailList( &RamdiskNonpagedPoolList, &myPool->ListEntry );
  709. KeReleaseSpinLock( &RamdiskPoolSpinLock, oldIrql );
  710. } else {
  711. KeEnterCriticalRegion();
  712. ExAcquireFastMutex( &RamdiskPoolMutex );
  713. InsertTailList( &RamdiskPagedPoolList, &myPool->ListEntry );
  714. ExReleaseFastMutex( &RamdiskPoolMutex );
  715. KeLeaveCriticalRegion();
  716. }
  717. //
  718. // Return a pointer to the caller's area, not to our header.
  719. //
  720. DBGPRINT( DBG_POOL, DBG_PAINFUL,
  721. ("Allocated %d bytes at %p for %s/%d\n", Size, myPool + 1, File, Line) );
  722. return myPool + 1;
  723. } // RamdiskAllocatePoolWithTag
  724. VOID
  725. RamdiskFreePool (
  726. PVOID Address,
  727. LOGICAL Private,
  728. PCHAR File,
  729. ULONG Line
  730. )
  731. {
  732. PMY_POOL myPool;
  733. PLIST_ENTRY list;
  734. PLIST_ENTRY listEntry;
  735. LOGICAL found;
  736. KIRQL oldIrql;
  737. //
  738. // The following line is here to get PREfast to stop complaining about the
  739. // call to KeReleaseSpinLock using an uninitialized variable.
  740. //
  741. oldIrql = 0;
  742. DBGPRINT( DBG_POOL, DBG_PAINFUL,
  743. ("Freeing pool at %p for %s/%d\n", Address, File, Line) );
  744. if ( !Private ) {
  745. //
  746. // This is not a private allocation (it was allocated by some other
  747. // piece of code). It doesn't have our header.
  748. //
  749. ExFreePool( Address );
  750. return;
  751. }
  752. //
  753. // Get the address of our header. Check that the header has our signature.
  754. //
  755. myPool = (PMY_POOL)Address - 1;
  756. if ( strcmp( myPool->Signature, MY_SIGNATURE ) != 0 ) {
  757. DbgPrint( "%s", "RAMDISK: Attempt to free pool block not owned by ramdisk.sys!!!\n" );
  758. DbgPrint( " address: %p, freeing file: %s, line: %d\n", Address, File, Line );
  759. ASSERT( FALSE );
  760. //
  761. // Since it doesn't look like our header, assume that it wasn't
  762. // really a private allocation.
  763. //
  764. ExFreePool( Address );
  765. return;
  766. }
  767. //
  768. // Remove the block from the allocation list. First, acquire the
  769. // appropriate lock.
  770. //
  771. if ( (myPool->Type & BASE_POOL_TYPE_MASK) == NonPagedPool ) {
  772. list = &RamdiskNonpagedPoolList;
  773. KeAcquireSpinLock( &RamdiskPoolSpinLock, &oldIrql );
  774. } else {
  775. list = &RamdiskPagedPoolList;
  776. KeEnterCriticalRegion();
  777. ExAcquireFastMutex( &RamdiskPoolMutex );
  778. }
  779. //
  780. // Search the list for this block.
  781. //
  782. found = FALSE;
  783. for ( listEntry = list->Flink;
  784. listEntry != list;
  785. listEntry = listEntry->Flink ) {
  786. if ( listEntry == &myPool->ListEntry ) {
  787. //
  788. // Found this block. Remove it from the list and leave the loop.
  789. //
  790. RemoveEntryList( listEntry );
  791. found = TRUE;
  792. break;
  793. }
  794. }
  795. //
  796. // Release the lock.
  797. //
  798. if ( (myPool->Type & BASE_POOL_TYPE_MASK) == NonPagedPool ) {
  799. KeReleaseSpinLock( &RamdiskPoolSpinLock, oldIrql );
  800. } else {
  801. ExReleaseFastMutex( &RamdiskPoolMutex );
  802. KeLeaveCriticalRegion();
  803. }
  804. if ( !found ) {
  805. //
  806. // Didn't find the block in the list. Complain.
  807. //
  808. DbgPrint( "%s", "RAMDISK: Attempt to free pool block not in allocation list!!!\n" );
  809. DbgPrint( " address: %p, freeing file: %s, line: %d\n", myPool, File, Line );
  810. DbgPrint( " allocating file: %s, line: %d\n", myPool->File, myPool->Line );
  811. ASSERT( FALSE );
  812. }
  813. //
  814. // Free the pool block.
  815. //
  816. ExFreePool( myPool );
  817. return;
  818. } // RamdiskFreePool
  819. #endif // defined(POOL_DBG)