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

special.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/special.c
00005  * PURPOSE:         ARM Memory Manager Special Pool implementation
00006  * PROGRAMMERS:     ReactOS Portable Systems Group
00007  */
00008 
00009 /*
00010     References:
00011     http://msdn.microsoft.com/en-us/library/ff551832(v=VS.85).aspx
00012 */
00013 
00014 /* INCLUDES *******************************************************************/
00015 
00016 #include <ntoskrnl.h>
00017 #define NDEBUG
00018 #include <debug.h>
00019 
00020 #define MODULE_INVOLVED_IN_ARM3
00021 #include "../ARM3/miarm.h"
00022 
00023 extern PMMPTE MmSystemPteBase;
00024 
00025 PMMPTE
00026 NTAPI
00027 MiReserveAlignedSystemPtes(IN ULONG NumberOfPtes,
00028                            IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType,
00029                            IN ULONG Alignment);
00030 
00031 /* GLOBALS ********************************************************************/
00032 
00033 #define SPECIAL_POOL_PAGED_PTE    0x2000
00034 #define SPECIAL_POOL_NONPAGED_PTE 0x4000
00035 #define SPECIAL_POOL_PAGED        0x8000
00036 
00037 PVOID MmSpecialPoolStart;
00038 PVOID MmSpecialPoolEnd;
00039 PVOID MiSpecialPoolExtra;
00040 ULONG MiSpecialPoolExtraCount;
00041 
00042 PMMPTE MiSpecialPoolFirstPte;
00043 PMMPTE MiSpecialPoolLastPte;
00044 
00045 PFN_NUMBER MiSpecialPagesNonPagedMaximum;
00046 
00047 BOOLEAN MmSpecialPoolCatchOverruns = TRUE;
00048 
00049 typedef struct _MI_FREED_SPECIAL_POOL
00050 {
00051     POOL_HEADER OverlaidPoolHeader;
00052     /* TODO: Add overlaid verifier pool header */
00053     ULONG Signature;
00054     ULONG TickCount;
00055     ULONG NumberOfBytesRequested;
00056     BOOLEAN Pagable;
00057     PVOID VirtualAddress;
00058     PVOID StackPointer;
00059     ULONG StackBytes;
00060     PETHREAD Thread;
00061     UCHAR StackData[0x400];
00062 } MI_FREED_SPECIAL_POOL, *PMI_FREED_SPECIAL_POOL;
00063 
00064 /* PRIVATE FUNCTIONS **********************************************************/
00065 
00066 VOID NTAPI MiTestSpecialPool();
00067 
00068 BOOLEAN
00069 NTAPI
00070 MmUseSpecialPool(SIZE_T NumberOfBytes, ULONG Tag)
00071 {
00072     /* Special pool is not suitable for allocations bigger than 1 page */
00073     if (NumberOfBytes > (PAGE_SIZE - sizeof(POOL_HEADER)))
00074         return FALSE;
00075 
00076     // FIXME
00077     //return TRUE;
00078     return FALSE;
00079 }
00080 
00081 BOOLEAN
00082 NTAPI
00083 MmIsSpecialPoolAddress(PVOID P)
00084 {
00085     return((P >= MmSpecialPoolStart) &&
00086            (P <= MmSpecialPoolEnd));
00087 }
00088 
00089 VOID
00090 NTAPI
00091 MiInitializeSpecialPool()
00092 {
00093     ULONG SpecialPoolPtes, i;
00094     PMMPTE PointerPte;
00095 
00096     /* Check if there is a special pool tag */
00097     if ((MmSpecialPoolTag == 0) ||
00098         (MmSpecialPoolTag == -1)) return;
00099 
00100     /* Calculate number of system PTEs for the special pool */
00101     if ( MmNumberOfSystemPtes >= 0x3000 )
00102         SpecialPoolPtes = MmNumberOfSystemPtes / 3;
00103     else
00104         SpecialPoolPtes = MmNumberOfSystemPtes / 6;
00105 
00106     /* Don't let the number go too high */
00107     if (SpecialPoolPtes > 0x6000) SpecialPoolPtes = 0x6000;
00108 
00109     /* Round up to the page size */
00110     SpecialPoolPtes = PAGE_ROUND_UP(SpecialPoolPtes);
00111 
00112     ASSERT((SpecialPoolPtes & (PTE_PER_PAGE - 1)) == 0);
00113 
00114     /* Reserve those PTEs */
00115     do
00116     {
00117         PointerPte = MiReserveAlignedSystemPtes(SpecialPoolPtes, 0, /*0x400000*/0); // FIXME:
00118         if (PointerPte) break;
00119 
00120         /* Reserving didn't work, so try to reduce the requested size */
00121         ASSERT(SpecialPoolPtes >= PTE_PER_PAGE);
00122         SpecialPoolPtes -= 1024;
00123     } while (SpecialPoolPtes);
00124 
00125     /* Fail if we couldn't reserve them at all */
00126     if (!SpecialPoolPtes) return;
00127 
00128     /* Make sure we got enough */
00129     ASSERT(SpecialPoolPtes >= PTE_PER_PAGE);
00130 
00131     /* Save first PTE and its address */
00132     MiSpecialPoolFirstPte = PointerPte;
00133     MmSpecialPoolStart = MiPteToAddress(PointerPte);
00134 
00135     for (i = 0; i<512; i++)
00136     {
00137         /* Point it to the next entry */
00138         PointerPte->u.List.NextEntry = &PointerPte[2] - MmSystemPteBase;
00139 
00140         /* Move to the next pair */
00141         PointerPte += 2;
00142     }
00143 
00144     /* Save extra values */
00145     MiSpecialPoolExtra = PointerPte;
00146     MiSpecialPoolExtraCount = SpecialPoolPtes - 1024;
00147 
00148     /* Mark the previous PTE as the last one */
00149     MiSpecialPoolLastPte = PointerPte - 2;
00150     MiSpecialPoolLastPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
00151 
00152     /* Save end address of the special pool */
00153     MmSpecialPoolEnd = MiPteToAddress(MiSpecialPoolLastPte + 1);
00154 
00155     /* Calculate maximum non-paged part of the special pool */
00156     MiSpecialPagesNonPagedMaximum = MmResidentAvailablePages >> 4;
00157 
00158     /* And limit it if it turned out to be too big */
00159     if (MmNumberOfPhysicalPages > 0x3FFF)
00160         MiSpecialPagesNonPagedMaximum = MmResidentAvailablePages >> 3;
00161 
00162     DPRINT1("Special pool start %p - end %p\n", MmSpecialPoolStart, MmSpecialPoolEnd);
00163 
00164     //MiTestSpecialPool();
00165 }
00166 
00167 PVOID
00168 NTAPI
00169 MmAllocateSpecialPool(SIZE_T NumberOfBytes, ULONG Tag, POOL_TYPE PoolType, ULONG SpecialType)
00170 {
00171     KIRQL Irql;
00172     MMPTE TempPte = ValidKernelPte;
00173     PMMPTE PointerPte;
00174     PFN_NUMBER PageFrameNumber;
00175     LARGE_INTEGER TickCount;
00176     PVOID Entry;
00177     PPOOL_HEADER Header;
00178 
00179     DPRINT1("MmAllocateSpecialPool(%x %x %x %x)\n", NumberOfBytes, Tag, PoolType, SpecialType);
00180 
00181     /* Check if the pool is initialized and quit if it's not */
00182     if (!MiSpecialPoolFirstPte) return NULL;
00183 
00184     /* Get the pool type */
00185     PoolType &= BASE_POOL_TYPE_MASK;
00186 
00187     /* Check whether current IRQL matches the pool type */
00188     Irql = KeGetCurrentIrql();
00189 
00190     if (((PoolType == PagedPool) && (Irql > APC_LEVEL)) ||
00191         ((PoolType != PagedPool) && (Irql > DISPATCH_LEVEL)))
00192     {
00193         /* Bad caller */
00194         KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION, Irql, PoolType, NumberOfBytes, 0x30);
00195     }
00196 
00197     /* TODO: Take into account various limitations */
00198     /*if ((PoolType != NonPagedPool) &&
00199         MiSpecialPagesNonPaged > MiSpecialPagesNonPagedMaximum)*/
00200 
00201     /* Lock PFN database */
00202     Irql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00203 
00204     /* Reject allocation in case amount of available pages is too small */
00205     if (MmAvailablePages < 0x100)
00206     {
00207         /* Release the PFN database lock */
00208         KeReleaseQueuedSpinLock(LockQueuePfnLock, Irql);
00209         DPRINT1("Special pool: MmAvailablePages 0x%x is too small\n", MmAvailablePages);
00210         return NULL;
00211     }
00212 
00213     /* Reject allocation if special pool PTE list is exhausted */
00214     if (MiSpecialPoolFirstPte->u.List.NextEntry == MM_EMPTY_PTE_LIST)
00215     {
00216         /* Release the PFN database lock */
00217         KeReleaseQueuedSpinLock(LockQueuePfnLock, Irql);
00218         DPRINT1("Special pool: No PTEs left!\n");
00219         /* TODO: Expand the special pool */
00220         return NULL;
00221     }
00222 
00223     /* Save allocation time */
00224     KeQueryTickCount(&TickCount);
00225 
00226     /* Get a pointer to the first PTE */
00227     PointerPte = MiSpecialPoolFirstPte;
00228 
00229     /* Set the first PTE pointer to the next one in the list */
00230     MiSpecialPoolFirstPte = MmSystemPteBase + PointerPte->u.List.NextEntry;
00231 
00232     /* Allocate a physical page */
00233     PageFrameNumber = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
00234 
00235     /* Initialize PFN and make it valid */
00236     TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
00237     MiInitializePfnAndMakePteValid(PageFrameNumber, PointerPte, TempPte);
00238 
00239     /* Release the PFN database lock */
00240     KeReleaseQueuedSpinLock(LockQueuePfnLock, Irql);
00241 
00242     /* Put some content into the page. Low value of tick count would do */
00243     Entry = MiPteToAddress(PointerPte);
00244     RtlFillMemory(Entry, PAGE_SIZE, TickCount.LowPart);
00245 
00246     /* Calculate header and entry addresses */
00247     if ((SpecialType != 0) &&
00248         ((SpecialType == 1) || (!MmSpecialPoolCatchOverruns)))
00249     {
00250         /* We catch underruns. Data is at the beginning of the page */
00251         Header = (PPOOL_HEADER)((PUCHAR)Entry + PAGE_SIZE - sizeof(POOL_HEADER));
00252     }
00253     else
00254     {
00255         /* We catch overruns. Data is at the end of the page */
00256         Header = (PPOOL_HEADER)Entry;
00257         Entry = (PVOID)((ULONG_PTR)((PUCHAR)Entry - NumberOfBytes + PAGE_SIZE) & ~((LONG_PTR)sizeof(POOL_HEADER) - 1));
00258     }
00259 
00260     /* Initialize the header */
00261     RtlZeroMemory(Header, sizeof(POOL_HEADER));
00262 
00263     /* Save allocation size there */
00264     Header->Ulong1 = (ULONG)NumberOfBytes;
00265 
00266     /* Make sure it's all good */
00267     ASSERT((NumberOfBytes <= PAGE_SIZE - sizeof(POOL_HEADER)) &&
00268            (PAGE_SIZE <= 32 * 1024));
00269 
00270     /* Mark it as paged or nonpaged */
00271     if (PoolType == PagedPool)
00272     {
00273         /* Add pagedpool flag into the pool header too */
00274         Header->Ulong1 |= SPECIAL_POOL_PAGED;
00275 
00276         /* Also mark the next PTE as special-pool-paged */
00277         PointerPte[1].u.Soft.PageFileHigh |= SPECIAL_POOL_PAGED_PTE;
00278     }
00279     else
00280     {
00281         /* Mark the next PTE as special-pool-nonpaged */
00282         PointerPte[1].u.Soft.PageFileHigh |= SPECIAL_POOL_NONPAGED_PTE;
00283     }
00284 
00285     /* Finally save tag and put allocation time into the header's blocksize.
00286        That time will be used to check memory consistency within the allocated
00287        page. */
00288     Header->PoolTag = Tag;
00289     Header->BlockSize = (USHORT)TickCount.LowPart;
00290     DPRINT1("%p\n", Entry);
00291     return Entry;
00292 }
00293 
00294 VOID
00295 NTAPI
00296 MiSpecialPoolCheckPattern(PUCHAR P, PPOOL_HEADER Header)
00297 {
00298     ULONG BytesToCheck, BytesRequested, Index;
00299     PUCHAR Ptr;
00300 
00301     /* Get amount of bytes user requested to be allocated by clearing out the paged mask */
00302     BytesRequested = (Header->Ulong1 & ~SPECIAL_POOL_PAGED) & 0xFFFF;
00303 
00304     /* Get a pointer to the end of user's area */
00305     Ptr = P + BytesRequested;
00306 
00307     /* Calculate how many bytes to check */
00308     BytesToCheck = (ULONG)((PUCHAR)PAGE_ALIGN(P) + PAGE_SIZE - Ptr);
00309 
00310     /* Remove pool header size if we're catching underruns */
00311     if (((ULONG_PTR)P & (PAGE_SIZE - 1)) == 0)
00312     {
00313         /* User buffer is located in the beginning of the page */
00314         BytesToCheck -= sizeof(POOL_HEADER);
00315     }
00316 
00317     /* Check the pattern after user buffer */
00318     for (Index = 0; Index < BytesToCheck; Index++)
00319     {
00320         /* Bugcheck if bytes don't match */
00321         if (Ptr[Index] != Header->BlockSize)
00322         {
00323             KeBugCheckEx(BAD_POOL_HEADER, (ULONG_PTR)P, (ULONG_PTR)&Ptr[Index], Header->BlockSize, 0x24);
00324         }
00325     }
00326 }
00327 
00328 VOID
00329 NTAPI
00330 MmFreeSpecialPool(PVOID P)
00331 {
00332     PMMPTE PointerPte;
00333     PPOOL_HEADER Header;
00334     BOOLEAN Overruns = FALSE;
00335     KIRQL Irql = KeGetCurrentIrql();
00336     POOL_TYPE PoolType;
00337     ULONG BytesRequested, BytesReal = 0;
00338     ULONG PtrOffset;
00339     PUCHAR b;
00340     PMI_FREED_SPECIAL_POOL FreedHeader;
00341     LARGE_INTEGER TickCount;
00342     PMMPFN Pfn;
00343 
00344     DPRINT1("MmFreeSpecialPool(%p)\n", P);
00345 
00346     /* Get the PTE */
00347     PointerPte = MiAddressToPte(P);
00348 
00349     /* Check if it's valid */
00350     if (PointerPte->u.Hard.Valid == 0)
00351     {
00352         /* Bugcheck if it has NOACCESS or 0 set as protection */
00353         if (PointerPte->u.Soft.Protection == MM_NOACCESS ||
00354             !PointerPte->u.Soft.Protection)
00355         {
00356             KeBugCheckEx(BAD_POOL_HEADER, (ULONG_PTR)P, (ULONG_PTR)PointerPte, 0, 0x20);
00357         }
00358     }
00359 
00360     /* Determine if it's a underruns or overruns pool pointer */
00361     PtrOffset = (ULONG)((ULONG_PTR)P & (PAGE_SIZE - 1));
00362     if (PtrOffset)
00363     {
00364         /* Pool catches overruns */
00365         Header = PAGE_ALIGN(P);
00366         Overruns = TRUE;
00367     }
00368     else
00369     {
00370         /* Pool catches underruns */
00371         Header = (PPOOL_HEADER)((PUCHAR)PAGE_ALIGN(P) + PAGE_SIZE - sizeof(POOL_HEADER));
00372     }
00373 
00374     /* Check if it's non paged pool */
00375     if ((Header->Ulong1 & SPECIAL_POOL_PAGED) == 0)
00376     {
00377         /* Non-paged allocation, ensure that IRQ is not higher that DISPATCH */
00378         ASSERT((PointerPte + 1)->u.Soft.PageFileHigh == SPECIAL_POOL_NONPAGED_PTE);
00379         if (Irql > DISPATCH_LEVEL)
00380         {
00381             KeBugCheckEx(BAD_POOL_HEADER, Irql, (ULONG_PTR)P, 0, 0x31);
00382         }
00383 
00384         PoolType = NonPagedPool;
00385     }
00386     else
00387     {
00388         /* Paged allocation, ensure */
00389         ASSERT((PointerPte + 1)->u.Soft.PageFileHigh == SPECIAL_POOL_PAGED_PTE);
00390         if (Irql > DISPATCH_LEVEL)
00391         {
00392             KeBugCheckEx(BAD_POOL_HEADER, Irql, (ULONG_PTR)P, 1, 0x31);
00393         }
00394 
00395         PoolType = PagedPool;
00396     }
00397 
00398     /* Get amount of bytes user requested to be allocated by clearing out the paged mask */
00399     BytesRequested = (Header->Ulong1 & ~SPECIAL_POOL_PAGED) & 0xFFFF;
00400 
00401     /* Check memory before the allocated user buffer in case of overruns detection */
00402     if (Overruns)
00403     {
00404         /* Calculate the real placement of the buffer */
00405         BytesReal = PAGE_SIZE - PtrOffset;
00406 
00407         /* If they mismatch, it's unrecoverable */
00408         if (BytesRequested > BytesReal)
00409         {
00410             KeBugCheckEx(BAD_POOL_HEADER, (ULONG_PTR)P, BytesRequested, BytesReal, 0x21);
00411         }
00412 
00413         if (BytesRequested + sizeof(POOL_HEADER) < BytesReal)
00414         {
00415             KeBugCheckEx(BAD_POOL_HEADER, (ULONG_PTR)P, BytesRequested, BytesReal, 0x22);
00416         }
00417 
00418         /* Actually check the memory pattern */
00419         for (b = (PUCHAR)(Header + 1); b < (PUCHAR)P; b++)
00420         {
00421             if (Header->BlockSize != b[0])
00422             {
00423                 /* Bytes mismatch */
00424                 KeBugCheckEx(BAD_POOL_HEADER, (ULONG_PTR)P, (ULONG_PTR)b, Header->BlockSize, 0x23);
00425             }
00426         }
00427     }
00428 
00429     /* Check the memory pattern after the user buffer */
00430     MiSpecialPoolCheckPattern(P, Header);
00431 
00432     /* Fill the freed header */
00433     KeQueryTickCount(&TickCount);
00434     FreedHeader = (PMI_FREED_SPECIAL_POOL)PAGE_ALIGN(P);
00435     FreedHeader->Signature = 0x98764321;
00436     FreedHeader->TickCount = TickCount.LowPart;
00437     FreedHeader->NumberOfBytesRequested = BytesRequested;
00438     FreedHeader->Pagable = PoolType;
00439     FreedHeader->VirtualAddress = P;
00440     FreedHeader->Thread = PsGetCurrentThread();
00441     /* TODO: Fill StackPointer and StackBytes */
00442     FreedHeader->StackPointer = NULL;
00443     FreedHeader->StackBytes = 0;
00444 
00445     if (PoolType == NonPagedPool)
00446     {
00447         /* Non pagable. Get PFN element corresponding to the PTE */
00448         Pfn = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
00449 
00450         /* Lock PFN database */
00451         ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
00452         Irql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00453 
00454         /* Delete this PFN */
00455         MI_SET_PFN_DELETED(Pfn);
00456 
00457         /* Decrement share count of this PFN */
00458         MiDecrementShareCount(Pfn, PointerPte->u.Hard.PageFrameNumber);
00459 
00460         /* Flush the TLB */
00461         //FIXME: Use KeFlushSingleTb() instead
00462         KeFlushEntireTb(TRUE, TRUE);
00463     }
00464     else
00465     {
00466         /* Pagable. Delete that virtual address */
00467         MiDeleteSystemPageableVm(PointerPte, 1, 0, NULL);
00468 
00469         /* Lock PFN database */
00470         ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
00471         Irql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00472     }
00473 
00474     /* Mark next PTE as invalid */
00475     PointerPte[1].u.Long = 0; //|= 8000;
00476 
00477     /* Make sure that the last entry is really the last one */
00478     ASSERT(MiSpecialPoolLastPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
00479 
00480     /* Update the current last PTE next pointer */
00481     MiSpecialPoolLastPte->u.List.NextEntry = PointerPte - MmSystemPteBase;
00482 
00483     /* PointerPte becomes the new last PTE */
00484     PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
00485     MiSpecialPoolLastPte = PointerPte;
00486 
00487     /* Release the PFN database lock */
00488     KeReleaseQueuedSpinLock(LockQueuePfnLock, Irql);
00489 }
00490 
00491 VOID
00492 NTAPI
00493 MiTestSpecialPool()
00494 {
00495     ULONG i;
00496     PVOID p1, p2[100];
00497     //PUCHAR p3;
00498     ULONG ByteSize;
00499     POOL_TYPE PoolType = PagedPool;
00500 
00501     // First allocate/free
00502     for (i=0; i<100; i++)
00503     {
00504         ByteSize = (100 * (i+1)) % (PAGE_SIZE - sizeof(POOL_HEADER));
00505         p1 = MmAllocateSpecialPool(ByteSize, 'TEST', PoolType, 0);
00506         DPRINT1("p1 %p size %d\n", p1, ByteSize);
00507         MmFreeSpecialPool(p1);
00508     }
00509 
00510     // Now allocate all at once, then free at once
00511     for (i=0; i<100; i++)
00512     {
00513         ByteSize = (100 * (i+1)) % (PAGE_SIZE - sizeof(POOL_HEADER));
00514         p2[i] = MmAllocateSpecialPool(ByteSize, 'TEST', PoolType, 0);
00515         DPRINT1("p2[%d] %p size %d\n", i, p1, ByteSize);
00516     }
00517     for (i=0; i<100; i++)
00518     {
00519         DPRINT1("Freeing %p\n", p2[i]);
00520         MmFreeSpecialPool(p2[i]);
00521     }
00522 
00523     // Overrun the buffer to test
00524     //ByteSize = 16;
00525     //p3 = MmAllocateSpecialPool(ByteSize, 'TEST', NonPagedPool, 0);
00526     //p3[ByteSize] = 0xF1; // This should cause an exception
00527 
00528     // Underrun the buffer to test
00529     //p3 = MmAllocateSpecialPool(ByteSize, 'TEST', NonPagedPool, 1);
00530     //p3--;
00531     //*p3 = 0xF1; // This should cause an exception
00532 
00533 }
00534 
00535 /* EOF */

Generated on Sun May 27 2012 04:37:36 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.