Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygencontmem.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: BSD - See COPYING.ARM in the top level directory 00004 * FILE: ntoskrnl/mm/ARM3/contmem.c 00005 * PURPOSE: ARM Memory Manager Contiguous Memory Allocator 00006 * PROGRAMMERS: ReactOS Portable Systems Group 00007 */ 00008 00009 /* INCLUDES *******************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 #define MODULE_INVOLVED_IN_ARM3 00016 #include "../ARM3/miarm.h" 00017 00018 /* PRIVATE FUNCTIONS **********************************************************/ 00019 00020 PFN_NUMBER 00021 NTAPI 00022 MiFindContiguousPages(IN PFN_NUMBER LowestPfn, 00023 IN PFN_NUMBER HighestPfn, 00024 IN PFN_NUMBER BoundaryPfn, 00025 IN PFN_NUMBER SizeInPages, 00026 IN MEMORY_CACHING_TYPE CacheType) 00027 { 00028 PFN_NUMBER Page, PageCount, LastPage, Length, BoundaryMask; 00029 ULONG i = 0; 00030 PMMPFN Pfn1, EndPfn; 00031 KIRQL OldIrql; 00032 PAGED_CODE(); 00033 ASSERT(SizeInPages != 0); 00034 00035 // 00036 // Convert the boundary PFN into an alignment mask 00037 // 00038 BoundaryMask = ~(BoundaryPfn - 1); 00039 00040 /* Disable APCs */ 00041 KeEnterGuardedRegion(); 00042 00043 // 00044 // Loop all the physical memory blocks 00045 // 00046 do 00047 { 00048 // 00049 // Capture the base page and length of this memory block 00050 // 00051 Page = MmPhysicalMemoryBlock->Run[i].BasePage; 00052 PageCount = MmPhysicalMemoryBlock->Run[i].PageCount; 00053 00054 // 00055 // Check how far this memory block will go 00056 // 00057 LastPage = Page + PageCount; 00058 00059 // 00060 // Trim it down to only the PFNs we're actually interested in 00061 // 00062 if ((LastPage - 1) > HighestPfn) LastPage = HighestPfn + 1; 00063 if (Page < LowestPfn) Page = LowestPfn; 00064 00065 // 00066 // Skip this run if it's empty or fails to contain all the pages we need 00067 // 00068 if (!(PageCount) || ((Page + SizeInPages) > LastPage)) continue; 00069 00070 // 00071 // Now scan all the relevant PFNs in this run 00072 // 00073 Length = 0; 00074 for (Pfn1 = MI_PFN_ELEMENT(Page); Page < LastPage; Page++, Pfn1++) 00075 { 00076 // 00077 // If this PFN is in use, ignore it 00078 // 00079 if (MiIsPfnInUse(Pfn1)) 00080 { 00081 Length = 0; 00082 continue; 00083 } 00084 00085 // 00086 // If we haven't chosen a start PFN yet and the caller specified an 00087 // alignment, make sure the page matches the alignment restriction 00088 // 00089 if ((!(Length) && (BoundaryPfn)) && 00090 (((Page ^ (Page + SizeInPages - 1)) & BoundaryMask))) 00091 { 00092 // 00093 // It does not, so bail out 00094 // 00095 continue; 00096 } 00097 00098 // 00099 // Increase the number of valid pages, and check if we have enough 00100 // 00101 if (++Length == SizeInPages) 00102 { 00103 // 00104 // It appears we've amassed enough legitimate pages, rollback 00105 // 00106 Pfn1 -= (Length - 1); 00107 Page -= (Length - 1); 00108 00109 // 00110 // Acquire the PFN lock 00111 // 00112 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00113 do 00114 { 00115 // 00116 // Things might've changed for us. Is the page still free? 00117 // 00118 if (MiIsPfnInUse(Pfn1)) break; 00119 00120 // 00121 // So far so good. Is this the last confirmed valid page? 00122 // 00123 if (!--Length) 00124 { 00125 // 00126 // Sanity check that we didn't go out of bounds 00127 // 00128 ASSERT(i != MmPhysicalMemoryBlock->NumberOfRuns); 00129 00130 // 00131 // Loop until all PFN entries have been processed 00132 // 00133 EndPfn = Pfn1 - SizeInPages + 1; 00134 do 00135 { 00136 // 00137 // This PFN is now a used page, set it up 00138 // 00139 MI_SET_USAGE(MI_USAGE_CONTINOUS_ALLOCATION); 00140 MI_SET_PROCESS2("Kernel Driver"); 00141 MiUnlinkFreeOrZeroedPage(Pfn1); 00142 Pfn1->u3.e2.ReferenceCount = 1; 00143 Pfn1->u2.ShareCount = 1; 00144 Pfn1->u3.e1.PageLocation = ActiveAndValid; 00145 Pfn1->u3.e1.StartOfAllocation = 0; 00146 Pfn1->u3.e1.EndOfAllocation = 0; 00147 Pfn1->u3.e1.PrototypePte = 0; 00148 Pfn1->u4.VerifierAllocation = 0; 00149 Pfn1->PteAddress = (PVOID)0xBAADF00D; 00150 00151 // 00152 // Check if this is the last PFN, otherwise go on 00153 // 00154 if (Pfn1 == EndPfn) break; 00155 Pfn1--; 00156 } while (TRUE); 00157 00158 // 00159 // Mark the first and last PFN so we can find them later 00160 // 00161 Pfn1->u3.e1.StartOfAllocation = 1; 00162 (Pfn1 + SizeInPages - 1)->u3.e1.EndOfAllocation = 1; 00163 00164 // 00165 // Now it's safe to let go of the PFN lock 00166 // 00167 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00168 00169 // 00170 // Quick sanity check that the last PFN is consistent 00171 // 00172 EndPfn = Pfn1 + SizeInPages; 00173 ASSERT(EndPfn == MI_PFN_ELEMENT(Page + 1)); 00174 00175 // 00176 // Compute the first page, and make sure it's consistent 00177 // 00178 Page = Page - SizeInPages + 1; 00179 ASSERT(Pfn1 == MI_PFN_ELEMENT(Page)); 00180 ASSERT(Page != 0); 00181 00182 /* Enable APCs and return the page */ 00183 KeLeaveGuardedRegion(); 00184 return Page; 00185 } 00186 00187 // 00188 // Keep going. The purpose of this loop is to reconfirm that 00189 // after acquiring the PFN lock these pages are still usable 00190 // 00191 Pfn1++; 00192 Page++; 00193 } while (TRUE); 00194 00195 // 00196 // If we got here, something changed while we hadn't acquired 00197 // the PFN lock yet, so we'll have to restart 00198 // 00199 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00200 Length = 0; 00201 } 00202 } 00203 } while (++i != MmPhysicalMemoryBlock->NumberOfRuns); 00204 00205 // 00206 // And if we get here, it means no suitable physical memory runs were found 00207 // 00208 return 0; 00209 } 00210 00211 PVOID 00212 NTAPI 00213 MiCheckForContiguousMemory(IN PVOID BaseAddress, 00214 IN PFN_NUMBER BaseAddressPages, 00215 IN PFN_NUMBER SizeInPages, 00216 IN PFN_NUMBER LowestPfn, 00217 IN PFN_NUMBER HighestPfn, 00218 IN PFN_NUMBER BoundaryPfn, 00219 IN MI_PFN_CACHE_ATTRIBUTE CacheAttribute) 00220 { 00221 PMMPTE StartPte, EndPte; 00222 PFN_NUMBER PreviousPage = 0, Page, HighPage, BoundaryMask, Pages = 0; 00223 00224 // 00225 // Okay, first of all check if the PFNs match our restrictions 00226 // 00227 if (LowestPfn > HighestPfn) return NULL; 00228 if (LowestPfn + SizeInPages <= LowestPfn) return NULL; 00229 if (LowestPfn + SizeInPages - 1 > HighestPfn) return NULL; 00230 if (BaseAddressPages < SizeInPages) return NULL; 00231 00232 // 00233 // This is the last page we need to get to and the boundary requested 00234 // 00235 HighPage = HighestPfn + 1 - SizeInPages; 00236 BoundaryMask = ~(BoundaryPfn - 1); 00237 00238 // 00239 // And here's the PTEs for this allocation. Let's go scan them. 00240 // 00241 StartPte = MiAddressToPte(BaseAddress); 00242 EndPte = StartPte + BaseAddressPages; 00243 while (StartPte < EndPte) 00244 { 00245 // 00246 // Get this PTE's page number 00247 // 00248 ASSERT (StartPte->u.Hard.Valid == 1); 00249 Page = PFN_FROM_PTE(StartPte); 00250 00251 // 00252 // Is this the beginning of our adventure? 00253 // 00254 if (!Pages) 00255 { 00256 // 00257 // Check if this PFN is within our range 00258 // 00259 if ((Page >= LowestPfn) && (Page <= HighPage)) 00260 { 00261 // 00262 // It is! Do you care about boundary (alignment)? 00263 // 00264 if (!(BoundaryPfn) || 00265 (!((Page ^ (Page + SizeInPages - 1)) & BoundaryMask))) 00266 { 00267 // 00268 // You don't care, or you do care but we deliver 00269 // 00270 Pages++; 00271 } 00272 } 00273 00274 // 00275 // Have we found all the pages we need by now? 00276 // Incidently, this means you only wanted one page 00277 // 00278 if (Pages == SizeInPages) 00279 { 00280 // 00281 // Mission complete 00282 // 00283 return MiPteToAddress(StartPte); 00284 } 00285 } 00286 else 00287 { 00288 // 00289 // Have we found a page that doesn't seem to be contiguous? 00290 // 00291 if (Page != (PreviousPage + 1)) 00292 { 00293 // 00294 // Ah crap, we have to start over 00295 // 00296 Pages = 0; 00297 continue; 00298 } 00299 00300 // 00301 // Otherwise, we're still in the game. Do we have all our pages? 00302 // 00303 if (++Pages == SizeInPages) 00304 { 00305 // 00306 // We do! This entire range was contiguous, so we'll return it! 00307 // 00308 return MiPteToAddress(StartPte - Pages + 1); 00309 } 00310 } 00311 00312 // 00313 // Try with the next PTE, remember this PFN 00314 // 00315 PreviousPage = Page; 00316 StartPte++; 00317 continue; 00318 } 00319 00320 // 00321 // All good returns are within the loop... 00322 // 00323 return NULL; 00324 } 00325 00326 PVOID 00327 NTAPI 00328 MiFindContiguousMemory(IN PFN_NUMBER LowestPfn, 00329 IN PFN_NUMBER HighestPfn, 00330 IN PFN_NUMBER BoundaryPfn, 00331 IN PFN_NUMBER SizeInPages, 00332 IN MEMORY_CACHING_TYPE CacheType) 00333 { 00334 PFN_NUMBER Page; 00335 PHYSICAL_ADDRESS PhysicalAddress; 00336 PMMPFN Pfn1, EndPfn; 00337 PMMPTE PointerPte; 00338 PVOID BaseAddress; 00339 PAGED_CODE(); 00340 ASSERT(SizeInPages != 0); 00341 00342 // 00343 // Our last hope is to scan the free page list for contiguous pages 00344 // 00345 Page = MiFindContiguousPages(LowestPfn, 00346 HighestPfn, 00347 BoundaryPfn, 00348 SizeInPages, 00349 CacheType); 00350 if (!Page) return NULL; 00351 00352 // 00353 // We'll just piggyback on the I/O memory mapper 00354 // 00355 PhysicalAddress.QuadPart = Page << PAGE_SHIFT; 00356 BaseAddress = MmMapIoSpace(PhysicalAddress, SizeInPages << PAGE_SHIFT, CacheType); 00357 ASSERT(BaseAddress); 00358 00359 /* Loop the PFN entries */ 00360 Pfn1 = MiGetPfnEntry(Page); 00361 EndPfn = Pfn1 + SizeInPages; 00362 PointerPte = MiAddressToPte(BaseAddress); 00363 do 00364 { 00365 /* Write the PTE address */ 00366 Pfn1->PteAddress = PointerPte; 00367 Pfn1->u4.PteFrame = PFN_FROM_PTE(MiAddressToPte(PointerPte++)); 00368 } while (++Pfn1 < EndPfn); 00369 00370 /* Return the address */ 00371 return BaseAddress; 00372 } 00373 00374 PVOID 00375 NTAPI 00376 MiAllocateContiguousMemory(IN SIZE_T NumberOfBytes, 00377 IN PFN_NUMBER LowestAcceptablePfn, 00378 IN PFN_NUMBER HighestAcceptablePfn, 00379 IN PFN_NUMBER BoundaryPfn, 00380 IN MEMORY_CACHING_TYPE CacheType) 00381 { 00382 PVOID BaseAddress; 00383 PFN_NUMBER SizeInPages; 00384 MI_PFN_CACHE_ATTRIBUTE CacheAttribute; 00385 00386 // 00387 // Verify count and cache type 00388 // 00389 ASSERT(NumberOfBytes != 0); 00390 ASSERT(CacheType <= MmWriteCombined); 00391 00392 // 00393 // Compute size requested 00394 // 00395 SizeInPages = BYTES_TO_PAGES(NumberOfBytes); 00396 00397 // 00398 // Convert the cache attribute and check for cached requests 00399 // 00400 CacheAttribute = MiPlatformCacheAttributes[FALSE][CacheType]; 00401 if (CacheAttribute == MiCached) 00402 { 00403 // 00404 // Because initial nonpaged pool is supposed to be contiguous, go ahead 00405 // and try making a nonpaged pool allocation first. 00406 // 00407 BaseAddress = ExAllocatePoolWithTag(NonPagedPoolCacheAligned, 00408 NumberOfBytes, 00409 'mCmM'); 00410 if (BaseAddress) 00411 { 00412 // 00413 // Now make sure it's actually contiguous (if it came from expansion 00414 // it might not be). 00415 // 00416 if (MiCheckForContiguousMemory(BaseAddress, 00417 SizeInPages, 00418 SizeInPages, 00419 LowestAcceptablePfn, 00420 HighestAcceptablePfn, 00421 BoundaryPfn, 00422 CacheAttribute)) 00423 { 00424 // 00425 // Sweet, we're in business! 00426 // 00427 return BaseAddress; 00428 } 00429 00430 // 00431 // No such luck 00432 // 00433 ExFreePoolWithTag(BaseAddress, 'mCmM'); 00434 } 00435 } 00436 00437 // 00438 // According to MSDN, the system won't try anything else if you're higher 00439 // than APC level. 00440 // 00441 if (KeGetCurrentIrql() > APC_LEVEL) return NULL; 00442 00443 // 00444 // Otherwise, we'll go try to find some 00445 // 00446 return MiFindContiguousMemory(LowestAcceptablePfn, 00447 HighestAcceptablePfn, 00448 BoundaryPfn, 00449 SizeInPages, 00450 CacheType); 00451 } 00452 00453 VOID 00454 NTAPI 00455 MiFreeContiguousMemory(IN PVOID BaseAddress) 00456 { 00457 KIRQL OldIrql; 00458 PFN_NUMBER PageFrameIndex, LastPage, PageCount; 00459 PMMPFN Pfn1, StartPfn; 00460 PMMPTE PointerPte; 00461 PAGED_CODE(); 00462 00463 // 00464 // First, check if the memory came from initial nonpaged pool, or expansion 00465 // 00466 if (((BaseAddress >= MmNonPagedPoolStart) && 00467 (BaseAddress < (PVOID)((ULONG_PTR)MmNonPagedPoolStart + 00468 MmSizeOfNonPagedPoolInBytes))) || 00469 ((BaseAddress >= MmNonPagedPoolExpansionStart) && 00470 (BaseAddress < MmNonPagedPoolEnd))) 00471 { 00472 // 00473 // It did, so just use the pool to free this 00474 // 00475 ExFreePoolWithTag(BaseAddress, 'mCmM'); 00476 return; 00477 } 00478 00479 /* Get the PTE and frame number for the allocation*/ 00480 PointerPte = MiAddressToPte(BaseAddress); 00481 PageFrameIndex = PFN_FROM_PTE(PointerPte); 00482 00483 // 00484 // Now get the PFN entry for this, and make sure it's the correct one 00485 // 00486 Pfn1 = MiGetPfnEntry(PageFrameIndex); 00487 if ((!Pfn1) || (Pfn1->u3.e1.StartOfAllocation == 0)) 00488 { 00489 // 00490 // This probably means you did a free on an address that was in between 00491 // 00492 KeBugCheckEx(BAD_POOL_CALLER, 00493 0x60, 00494 (ULONG_PTR)BaseAddress, 00495 0, 00496 0); 00497 } 00498 00499 // 00500 // Now this PFN isn't the start of any allocation anymore, it's going out 00501 // 00502 StartPfn = Pfn1; 00503 Pfn1->u3.e1.StartOfAllocation = 0; 00504 00505 /* Loop the PFNs until we find the one that marks the end of the allocation */ 00506 do 00507 { 00508 /* Make sure these are the pages we setup in the allocation routine */ 00509 ASSERT(Pfn1->u3.e2.ReferenceCount == 1); 00510 ASSERT(Pfn1->u2.ShareCount == 1); 00511 ASSERT(Pfn1->PteAddress == PointerPte); 00512 ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid); 00513 ASSERT(Pfn1->u4.VerifierAllocation == 0); 00514 ASSERT(Pfn1->u3.e1.PrototypePte == 0); 00515 00516 /* Set the special pending delete marker */ 00517 MI_SET_PFN_DELETED(Pfn1); 00518 00519 /* Keep going for assertions */ 00520 PointerPte++; 00521 } while (Pfn1++->u3.e1.EndOfAllocation == 0); 00522 00523 // 00524 // Found it, unmark it 00525 // 00526 Pfn1--; 00527 Pfn1->u3.e1.EndOfAllocation = 0; 00528 00529 // 00530 // Now compute how many pages this represents 00531 // 00532 PageCount = (ULONG)(Pfn1 - StartPfn + 1); 00533 00534 // 00535 // So we can know how much to unmap (recall we piggyback on I/O mappings) 00536 // 00537 MmUnmapIoSpace(BaseAddress, PageCount << PAGE_SHIFT); 00538 00539 // 00540 // Lock the PFN database 00541 // 00542 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00543 00544 // 00545 // Loop all the pages 00546 // 00547 LastPage = PageFrameIndex + PageCount; 00548 Pfn1 = MiGetPfnEntry(PageFrameIndex); 00549 do 00550 { 00551 /* Decrement the share count and move on */ 00552 MiDecrementShareCount(Pfn1++, PageFrameIndex++); 00553 } while (PageFrameIndex < LastPage); 00554 00555 // 00556 // Release the PFN lock 00557 // 00558 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00559 } 00560 00561 /* PUBLIC FUNCTIONS ***********************************************************/ 00562 00563 /* 00564 * @implemented 00565 */ 00566 PVOID 00567 NTAPI 00568 MmAllocateContiguousMemorySpecifyCache(IN SIZE_T NumberOfBytes, 00569 IN PHYSICAL_ADDRESS LowestAcceptableAddress OPTIONAL, 00570 IN PHYSICAL_ADDRESS HighestAcceptableAddress, 00571 IN PHYSICAL_ADDRESS BoundaryAddressMultiple OPTIONAL, 00572 IN MEMORY_CACHING_TYPE CacheType OPTIONAL) 00573 { 00574 PFN_NUMBER LowestPfn, HighestPfn, BoundaryPfn; 00575 00576 // 00577 // Verify count and cache type 00578 // 00579 ASSERT(NumberOfBytes != 0); 00580 ASSERT(CacheType <= MmWriteCombined); 00581 00582 // 00583 // Convert the lowest address into a PFN 00584 // 00585 LowestPfn = (PFN_NUMBER)(LowestAcceptableAddress.QuadPart >> PAGE_SHIFT); 00586 if (BYTE_OFFSET(LowestAcceptableAddress.LowPart)) LowestPfn++; 00587 00588 // 00589 // Convert and validate the boundary address into a PFN 00590 // 00591 if (BYTE_OFFSET(BoundaryAddressMultiple.LowPart)) return NULL; 00592 BoundaryPfn = (PFN_NUMBER)(BoundaryAddressMultiple.QuadPart >> PAGE_SHIFT); 00593 00594 // 00595 // Convert the highest address into a PFN 00596 // 00597 HighestPfn = (PFN_NUMBER)(HighestAcceptableAddress.QuadPart >> PAGE_SHIFT); 00598 if (HighestPfn > MmHighestPhysicalPage) HighestPfn = MmHighestPhysicalPage; 00599 00600 // 00601 // Validate the PFN bounds 00602 // 00603 if (LowestPfn > HighestPfn) return NULL; 00604 00605 // 00606 // Let the contiguous memory allocator handle it 00607 // 00608 return MiAllocateContiguousMemory(NumberOfBytes, 00609 LowestPfn, 00610 HighestPfn, 00611 BoundaryPfn, 00612 CacheType); 00613 } 00614 00615 /* 00616 * @implemented 00617 */ 00618 PVOID 00619 NTAPI 00620 MmAllocateContiguousMemory(IN SIZE_T NumberOfBytes, 00621 IN PHYSICAL_ADDRESS HighestAcceptableAddress) 00622 { 00623 PFN_NUMBER HighestPfn; 00624 00625 // 00626 // Verify byte count 00627 // 00628 ASSERT(NumberOfBytes != 0); 00629 00630 // 00631 // Convert and normalize the highest address into a PFN 00632 // 00633 HighestPfn = (PFN_NUMBER)(HighestAcceptableAddress.QuadPart >> PAGE_SHIFT); 00634 if (HighestPfn > MmHighestPhysicalPage) HighestPfn = MmHighestPhysicalPage; 00635 00636 // 00637 // Let the contiguous memory allocator handle it 00638 // 00639 return MiAllocateContiguousMemory(NumberOfBytes, 0, HighestPfn, 0, MmCached); 00640 } 00641 00642 /* 00643 * @implemented 00644 */ 00645 VOID 00646 NTAPI 00647 MmFreeContiguousMemory(IN PVOID BaseAddress) 00648 { 00649 // 00650 // Let the contiguous memory allocator handle it 00651 // 00652 MiFreeContiguousMemory(BaseAddress); 00653 } 00654 00655 /* 00656 * @implemented 00657 */ 00658 VOID 00659 NTAPI 00660 MmFreeContiguousMemorySpecifyCache(IN PVOID BaseAddress, 00661 IN SIZE_T NumberOfBytes, 00662 IN MEMORY_CACHING_TYPE CacheType) 00663 { 00664 // 00665 // Just call the non-cached version (there's no cache issues for freeing) 00666 // 00667 MiFreeContiguousMemory(BaseAddress); 00668 } 00669 00670 /* EOF */ Generated on Sat May 26 2012 04:36:21 for ReactOS by
1.7.6.1
|