Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenspecial.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
1.7.6.1
|