ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

contmem.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.