xref: /reactos/ntoskrnl/mm/ARM3/special.c (revision 40462c92)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/special.c
5  * PURPOSE:         ARM Memory Manager Special Pool implementation
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /*
10     References:
11     http://msdn.microsoft.com/en-us/library/ff551832(v=VS.85).aspx
12 */
13 
14 /* INCLUDES *******************************************************************/
15 
16 #include <ntoskrnl.h>
17 #define NDEBUG
18 #include <debug.h>
19 
20 #define MODULE_INVOLVED_IN_ARM3
21 #include <mm/ARM3/miarm.h>
22 
23 extern ULONG ExpPoolFlags;
24 extern PMMPTE MmSystemPteBase;
25 
26 PMMPTE
27 NTAPI
28 MiReserveAlignedSystemPtes(IN ULONG NumberOfPtes,
29                            IN MMSYSTEM_PTE_POOL_TYPE SystemPtePoolType,
30                            IN ULONG Alignment);
31 
32 /* GLOBALS ********************************************************************/
33 
34 #define SPECIAL_POOL_PAGED_PTE    0x2000
35 #define SPECIAL_POOL_NONPAGED_PTE 0x4000
36 #define SPECIAL_POOL_PAGED        0x8000
37 
38 PVOID MmSpecialPoolStart;
39 PVOID MmSpecialPoolEnd;
40 PVOID MiSpecialPoolExtra;
41 ULONG MiSpecialPoolExtraCount;
42 
43 PMMPTE MiSpecialPoolFirstPte;
44 PMMPTE MiSpecialPoolLastPte;
45 
46 PFN_COUNT MmSpecialPagesInUse;
47 PFN_COUNT MmSpecialPagesInUsePeak;
48 PFN_COUNT MiSpecialPagesPagable;
49 PFN_COUNT MiSpecialPagesPagablePeak;
50 PFN_COUNT MiSpecialPagesNonPaged;
51 PFN_COUNT MiSpecialPagesNonPagedPeak;
52 PFN_COUNT MiSpecialPagesNonPagedMaximum;
53 
54 BOOLEAN MmSpecialPoolCatchOverruns = TRUE;
55 
56 typedef struct _MI_FREED_SPECIAL_POOL
57 {
58     POOL_HEADER OverlaidPoolHeader;
59     /* TODO: Add overlaid verifier pool header */
60     ULONG Signature;
61     ULONG TickCount;
62     ULONG NumberOfBytesRequested;
63     BOOLEAN Pagable;
64     PVOID VirtualAddress;
65     PVOID StackPointer;
66     ULONG StackBytes;
67     PETHREAD Thread;
68     UCHAR StackData[0x400];
69 } MI_FREED_SPECIAL_POOL, *PMI_FREED_SPECIAL_POOL;
70 
71 /* PRIVATE FUNCTIONS **********************************************************/
72 
73 VOID NTAPI MiTestSpecialPool(VOID);
74 
75 BOOLEAN
76 NTAPI
77 MmUseSpecialPool(SIZE_T NumberOfBytes, ULONG Tag)
78 {
79     /* Special pool is not suitable for allocations bigger than 1 page */
80     if (NumberOfBytes > (PAGE_SIZE - sizeof(POOL_HEADER)))
81     {
82         return FALSE;
83     }
84 
85     if (MmSpecialPoolTag == '*')
86     {
87         return TRUE;
88     }
89 
90     return Tag == MmSpecialPoolTag;
91 }
92 
93 BOOLEAN
94 NTAPI
95 MmIsSpecialPoolAddress(PVOID P)
96 {
97     return ((P >= MmSpecialPoolStart) &&
98             (P <= MmSpecialPoolEnd));
99 }
100 
101 BOOLEAN
102 NTAPI
103 MmIsSpecialPoolAddressFree(PVOID P)
104 {
105     PMMPTE PointerPte;
106 
107     ASSERT(MmIsSpecialPoolAddress(P));
108     PointerPte = MiAddressToPte(P);
109 
110     if (PointerPte->u.Soft.PageFileHigh == SPECIAL_POOL_PAGED_PTE ||
111         PointerPte->u.Soft.PageFileHigh == SPECIAL_POOL_NONPAGED_PTE)
112     {
113         /* Guard page PTE */
114         return FALSE;
115     }
116 
117     /* Free PTE */
118     return TRUE;
119 }
120 
121 VOID
122 NTAPI
123 MiInitializeSpecialPool(VOID)
124 {
125     ULONG SpecialPoolPtes, i;
126     PMMPTE PointerPte;
127 
128     /* Check if there is a special pool tag */
129     if ((MmSpecialPoolTag == 0) ||
130         (MmSpecialPoolTag == -1)) return;
131 
132     /* Calculate number of system PTEs for the special pool */
133     if (MmNumberOfSystemPtes >= 0x3000)
134         SpecialPoolPtes = MmNumberOfSystemPtes / 3;
135     else
136         SpecialPoolPtes = MmNumberOfSystemPtes / 6;
137 
138     /* Don't let the number go too high */
139     if (SpecialPoolPtes > 0x6000) SpecialPoolPtes = 0x6000;
140 
141     /* Round up to the page size */
142     SpecialPoolPtes = PAGE_ROUND_UP(SpecialPoolPtes);
143 
144     ASSERT((SpecialPoolPtes & (PTE_PER_PAGE - 1)) == 0);
145 
146     /* Reserve those PTEs */
147     do
148     {
149         PointerPte = MiReserveAlignedSystemPtes(SpecialPoolPtes,
150                                                 SystemPteSpace,
151                                                 /*0x400000*/0); // FIXME:
152         if (PointerPte) break;
153 
154         /* Reserving didn't work, so try to reduce the requested size */
155         ASSERT(SpecialPoolPtes >= PTE_PER_PAGE);
156         SpecialPoolPtes -= PTE_PER_PAGE;
157     } while (SpecialPoolPtes);
158 
159     /* Fail if we couldn't reserve them at all */
160     if (!SpecialPoolPtes) return;
161 
162     /* Make sure we got enough */
163     ASSERT(SpecialPoolPtes >= PTE_PER_PAGE);
164 
165     /* Save first PTE and its address */
166     MiSpecialPoolFirstPte = PointerPte;
167     MmSpecialPoolStart = MiPteToAddress(PointerPte);
168 
169     for (i = 0; i < PTE_PER_PAGE / 2; i++)
170     {
171         /* Point it to the next entry */
172         PointerPte->u.List.NextEntry = &PointerPte[2] - MmSystemPteBase;
173 
174         /* Move to the next pair */
175         PointerPte += 2;
176     }
177 
178     /* Save extra values */
179     MiSpecialPoolExtra = PointerPte;
180     MiSpecialPoolExtraCount = SpecialPoolPtes - PTE_PER_PAGE;
181 
182     /* Mark the previous PTE as the last one */
183     MiSpecialPoolLastPte = PointerPte - 2;
184     MiSpecialPoolLastPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
185 
186     /* Save end address of the special pool */
187     MmSpecialPoolEnd = MiPteToAddress(MiSpecialPoolLastPte + 1);
188 
189     /* Calculate maximum non-paged part of the special pool */
190     MiSpecialPagesNonPagedMaximum = MmResidentAvailablePages >> 4;
191 
192     /* And limit it if it turned out to be too big */
193     if (MmNumberOfPhysicalPages > 0x3FFF)
194         MiSpecialPagesNonPagedMaximum = MmResidentAvailablePages >> 3;
195 
196     DPRINT1("Special pool start %p - end %p\n", MmSpecialPoolStart, MmSpecialPoolEnd);
197     ExpPoolFlags |= POOL_FLAG_SPECIAL_POOL;
198 
199     //MiTestSpecialPool();
200 }
201 
202 NTSTATUS
203 NTAPI
204 MmExpandSpecialPool(VOID)
205 {
206     ULONG i;
207     PMMPTE PointerPte;
208 
209     MI_ASSERT_PFN_LOCK_HELD();
210 
211     if (MiSpecialPoolExtraCount == 0)
212         return STATUS_INSUFFICIENT_RESOURCES;
213 
214     PointerPte = MiSpecialPoolExtra;
215     ASSERT(MiSpecialPoolFirstPte == MiSpecialPoolLastPte);
216     ASSERT(MiSpecialPoolFirstPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
217     MiSpecialPoolFirstPte->u.List.NextEntry = PointerPte - MmSystemPteBase;
218 
219     ASSERT(MiSpecialPoolExtraCount >= PTE_PER_PAGE);
220     for (i = 0; i < PTE_PER_PAGE / 2; i++)
221     {
222         /* Point it to the next entry */
223         PointerPte->u.List.NextEntry = &PointerPte[2] - MmSystemPteBase;
224 
225         /* Move to the next pair */
226         PointerPte += 2;
227     }
228 
229     /* Save remaining extra values */
230     MiSpecialPoolExtra = PointerPte;
231     MiSpecialPoolExtraCount -= PTE_PER_PAGE;
232 
233     /* Mark the previous PTE as the last one */
234     MiSpecialPoolLastPte = PointerPte - 2;
235     MiSpecialPoolLastPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
236 
237     /* Save new end address of the special pool */
238     MmSpecialPoolEnd = MiPteToAddress(MiSpecialPoolLastPte + 1);
239 
240     return STATUS_SUCCESS;
241 }
242 
243 PVOID
244 NTAPI
245 MmAllocateSpecialPool(SIZE_T NumberOfBytes, ULONG Tag, POOL_TYPE PoolType, ULONG SpecialType)
246 {
247     KIRQL Irql;
248     MMPTE TempPte = ValidKernelPte;
249     PMMPTE PointerPte;
250     PFN_NUMBER PageFrameNumber;
251     LARGE_INTEGER TickCount;
252     PVOID Entry;
253     PPOOL_HEADER Header;
254     PFN_COUNT PagesInUse;
255 
256     DPRINT("MmAllocateSpecialPool(%x %x %x %x)\n", NumberOfBytes, Tag, PoolType, SpecialType);
257 
258     /* Check if the pool is initialized and quit if it's not */
259     if (!MiSpecialPoolFirstPte) return NULL;
260 
261     /* Get the pool type */
262     PoolType &= BASE_POOL_TYPE_MASK;
263 
264     /* Check whether current IRQL matches the pool type */
265     Irql = KeGetCurrentIrql();
266 
267     if (((PoolType == PagedPool) && (Irql > APC_LEVEL)) ||
268         ((PoolType != PagedPool) && (Irql > DISPATCH_LEVEL)))
269     {
270         /* Bad caller */
271         KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
272                      Irql,
273                      PoolType,
274                      NumberOfBytes,
275                      0x30);
276     }
277 
278     /* Some allocations from Mm must never use special pool */
279     if (Tag == 'tSmM')
280     {
281         /* Reject and let normal pool handle it */
282         return NULL;
283     }
284 
285     /* TODO: Take into account various limitations */
286 
287     /* Heed the maximum limit of nonpaged pages */
288     if ((PoolType == NonPagedPool) &&
289         (MiSpecialPagesNonPaged > MiSpecialPagesNonPagedMaximum))
290     {
291         return NULL;
292     }
293 
294     /* Lock PFN database */
295     Irql = MiAcquirePfnLock();
296 
297     /* Reject allocation in case amount of available pages is too small */
298     if (MmAvailablePages < 0x100)
299     {
300         /* Release the PFN database lock */
301         MiReleasePfnLock(Irql);
302         DPRINT1("Special pool: MmAvailablePages 0x%x is too small\n", MmAvailablePages);
303         return NULL;
304     }
305 
306     /* Check if special pool PTE list is exhausted */
307     if (MiSpecialPoolFirstPte->u.List.NextEntry == MM_EMPTY_PTE_LIST)
308     {
309         /* Try to expand it */
310         if (!NT_SUCCESS(MmExpandSpecialPool()))
311         {
312             /* No reserves left, reject this allocation */
313             static int once;
314             MiReleasePfnLock(Irql);
315             if (!once++) DPRINT1("Special pool: No PTEs left!\n");
316             return NULL;
317         }
318         ASSERT(MiSpecialPoolFirstPte->u.List.NextEntry != MM_EMPTY_PTE_LIST);
319     }
320 
321     /* Save allocation time */
322     KeQueryTickCount(&TickCount);
323 
324     /* Get a pointer to the first PTE */
325     PointerPte = MiSpecialPoolFirstPte;
326 
327     /* Set the first PTE pointer to the next one in the list */
328     MiSpecialPoolFirstPte = MmSystemPteBase + PointerPte->u.List.NextEntry;
329 
330     /* Allocate a physical page */
331     if (PoolType == PagedPool)
332     {
333         MI_SET_USAGE(MI_USAGE_PAGED_POOL);
334     }
335     else
336     {
337         MI_SET_USAGE(MI_USAGE_NONPAGED_POOL);
338     }
339     MI_SET_PROCESS2("Kernel-Special");
340     PageFrameNumber = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
341 
342     /* Initialize PFN and make it valid */
343     TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
344     MiInitializePfnAndMakePteValid(PageFrameNumber, PointerPte, TempPte);
345 
346     /* Release the PFN database lock */
347     MiReleasePfnLock(Irql);
348 
349     /* Increase page counter */
350     PagesInUse = InterlockedIncrementUL(&MmSpecialPagesInUse);
351     if (PagesInUse > MmSpecialPagesInUsePeak)
352         MmSpecialPagesInUsePeak = PagesInUse;
353 
354     /* Put some content into the page. Low value of tick count would do */
355     Entry = MiPteToAddress(PointerPte);
356     RtlFillMemory(Entry, PAGE_SIZE, TickCount.LowPart);
357 
358     /* Calculate header and entry addresses */
359     if ((SpecialType != 0) &&
360         ((SpecialType == 1) || (!MmSpecialPoolCatchOverruns)))
361     {
362         /* We catch underruns. Data is at the beginning of the page */
363         Header = (PPOOL_HEADER)((PUCHAR)Entry + PAGE_SIZE - sizeof(POOL_HEADER));
364     }
365     else
366     {
367         /* We catch overruns. Data is at the end of the page */
368         Header = (PPOOL_HEADER)Entry;
369         Entry = (PVOID)((ULONG_PTR)((PUCHAR)Entry - NumberOfBytes + PAGE_SIZE) & ~((LONG_PTR)sizeof(POOL_HEADER) - 1));
370     }
371 
372     /* Initialize the header */
373     RtlZeroMemory(Header, sizeof(POOL_HEADER));
374 
375     /* Save allocation size there */
376     Header->Ulong1 = (ULONG)NumberOfBytes;
377 
378     /* Make sure it's all good */
379     ASSERT((NumberOfBytes <= PAGE_SIZE - sizeof(POOL_HEADER)) &&
380            (PAGE_SIZE <= 32 * 1024));
381 
382     /* Mark it as paged or nonpaged */
383     if (PoolType == PagedPool)
384     {
385         /* Add pagedpool flag into the pool header too */
386         Header->Ulong1 |= SPECIAL_POOL_PAGED;
387 
388         /* Also mark the next PTE as special-pool-paged */
389         PointerPte[1].u.Soft.PageFileHigh |= SPECIAL_POOL_PAGED_PTE;
390 
391         /* Increase pagable counter */
392         PagesInUse = InterlockedIncrementUL(&MiSpecialPagesPagable);
393         if (PagesInUse > MiSpecialPagesPagablePeak)
394             MiSpecialPagesPagablePeak = PagesInUse;
395     }
396     else
397     {
398         /* Mark the next PTE as special-pool-nonpaged */
399         PointerPte[1].u.Soft.PageFileHigh |= SPECIAL_POOL_NONPAGED_PTE;
400 
401         /* Increase nonpaged counter */
402         PagesInUse = InterlockedIncrementUL(&MiSpecialPagesNonPaged);
403         if (PagesInUse > MiSpecialPagesNonPagedPeak)
404             MiSpecialPagesNonPagedPeak = PagesInUse;
405     }
406 
407     /* Finally save tag and put allocation time into the header's blocksize.
408        That time will be used to check memory consistency within the allocated
409        page. */
410     Header->PoolTag = Tag;
411     Header->BlockSize = (UCHAR)TickCount.LowPart;
412     DPRINT("%p\n", Entry);
413     return Entry;
414 }
415 
416 VOID
417 NTAPI
418 MiSpecialPoolCheckPattern(PUCHAR P, PPOOL_HEADER Header)
419 {
420     ULONG BytesToCheck, BytesRequested, Index;
421     PUCHAR Ptr;
422 
423     /* Get amount of bytes user requested to be allocated by clearing out the paged mask */
424     BytesRequested = (Header->Ulong1 & ~SPECIAL_POOL_PAGED) & 0xFFFF;
425     ASSERT(BytesRequested <= PAGE_SIZE - sizeof(POOL_HEADER));
426 
427     /* Get a pointer to the end of user's area */
428     Ptr = P + BytesRequested;
429 
430     /* Calculate how many bytes to check */
431     BytesToCheck = (ULONG)((PUCHAR)PAGE_ALIGN(P) + PAGE_SIZE - Ptr);
432 
433     /* Remove pool header size if we're catching underruns */
434     if (((ULONG_PTR)P & (PAGE_SIZE - 1)) == 0)
435     {
436         /* User buffer is located in the beginning of the page */
437         BytesToCheck -= sizeof(POOL_HEADER);
438     }
439 
440     /* Check the pattern after user buffer */
441     for (Index = 0; Index < BytesToCheck; Index++)
442     {
443         /* Bugcheck if bytes don't match */
444         if (Ptr[Index] != Header->BlockSize)
445         {
446             KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
447                          (ULONG_PTR)P,
448                          (ULONG_PTR)&Ptr[Index],
449                          Header->BlockSize,
450                          0x24);
451         }
452     }
453 }
454 
455 VOID
456 NTAPI
457 MmFreeSpecialPool(PVOID P)
458 {
459     PMMPTE PointerPte;
460     PPOOL_HEADER Header;
461     BOOLEAN Overruns = FALSE;
462     KIRQL Irql = KeGetCurrentIrql();
463     POOL_TYPE PoolType;
464     ULONG BytesRequested, BytesReal = 0;
465     ULONG PtrOffset;
466     PUCHAR b;
467     PMI_FREED_SPECIAL_POOL FreedHeader;
468     LARGE_INTEGER TickCount;
469     PMMPFN Pfn;
470 
471     DPRINT("MmFreeSpecialPool(%p)\n", P);
472 
473     /* Get the PTE */
474     PointerPte = MiAddressToPte(P);
475 
476     /* Check if it's valid */
477     if (PointerPte->u.Hard.Valid == 0)
478     {
479         /* Bugcheck if it has NOACCESS or 0 set as protection */
480         if (PointerPte->u.Soft.Protection == MM_NOACCESS ||
481             !PointerPte->u.Soft.Protection)
482         {
483             KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
484                          (ULONG_PTR)P,
485                          (ULONG_PTR)PointerPte,
486                          0,
487                          0x20);
488         }
489     }
490 
491     /* Determine if it's a underruns or overruns pool pointer */
492     PtrOffset = (ULONG)((ULONG_PTR)P & (PAGE_SIZE - 1));
493     if (PtrOffset)
494     {
495         /* Pool catches overruns */
496         Header = PAGE_ALIGN(P);
497         Overruns = TRUE;
498     }
499     else
500     {
501         /* Pool catches underruns */
502         Header = (PPOOL_HEADER)((PUCHAR)PAGE_ALIGN(P) + PAGE_SIZE - sizeof(POOL_HEADER));
503     }
504 
505     /* Check if it's non paged pool */
506     if ((Header->Ulong1 & SPECIAL_POOL_PAGED) == 0)
507     {
508         /* Non-paged allocation, ensure that IRQ is not higher that DISPATCH */
509         PoolType = NonPagedPool;
510         ASSERT(PointerPte[1].u.Soft.PageFileHigh == SPECIAL_POOL_NONPAGED_PTE);
511         if (Irql > DISPATCH_LEVEL)
512         {
513             KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
514                          Irql,
515                          PoolType,
516                          (ULONG_PTR)P,
517                          0x31);
518         }
519     }
520     else
521     {
522         /* Paged allocation, ensure */
523         PoolType = PagedPool;
524         ASSERT(PointerPte[1].u.Soft.PageFileHigh == SPECIAL_POOL_PAGED_PTE);
525         if (Irql > APC_LEVEL)
526         {
527             KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
528                          Irql,
529                          PoolType,
530                          (ULONG_PTR)P,
531                          0x31);
532         }
533     }
534 
535     /* Get amount of bytes user requested to be allocated by clearing out the paged mask */
536     BytesRequested = (Header->Ulong1 & ~SPECIAL_POOL_PAGED) & 0xFFFF;
537     ASSERT(BytesRequested <= PAGE_SIZE - sizeof(POOL_HEADER));
538 
539     /* Check memory before the allocated user buffer in case of overruns detection */
540     if (Overruns)
541     {
542         /* Calculate the real placement of the buffer */
543         BytesReal = PAGE_SIZE - PtrOffset;
544 
545         /* If they mismatch, it's unrecoverable */
546         if (BytesRequested > BytesReal)
547         {
548             KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
549                          (ULONG_PTR)P,
550                          BytesRequested,
551                          BytesReal,
552                          0x21);
553         }
554 
555         if (BytesRequested + sizeof(POOL_HEADER) < BytesReal)
556         {
557             KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
558                          (ULONG_PTR)P,
559                          BytesRequested,
560                          BytesReal,
561                          0x22);
562         }
563 
564         /* Actually check the memory pattern */
565         for (b = (PUCHAR)(Header + 1); b < (PUCHAR)P; b++)
566         {
567             if (*b != Header->BlockSize)
568             {
569                 /* Bytes mismatch */
570                 KeBugCheckEx(SPECIAL_POOL_DETECTED_MEMORY_CORRUPTION,
571                              (ULONG_PTR)P,
572                              (ULONG_PTR)b,
573                              Header->BlockSize,
574                              0x23);
575             }
576         }
577     }
578 
579     /* Check the memory pattern after the user buffer */
580     MiSpecialPoolCheckPattern(P, Header);
581 
582     /* Fill the freed header */
583     KeQueryTickCount(&TickCount);
584     FreedHeader = (PMI_FREED_SPECIAL_POOL)PAGE_ALIGN(P);
585     FreedHeader->Signature = 0x98764321;
586     FreedHeader->TickCount = TickCount.LowPart;
587     FreedHeader->NumberOfBytesRequested = BytesRequested;
588     FreedHeader->Pagable = PoolType;
589     FreedHeader->VirtualAddress = P;
590     FreedHeader->Thread = PsGetCurrentThread();
591     /* TODO: Fill StackPointer and StackBytes */
592     FreedHeader->StackPointer = NULL;
593     FreedHeader->StackBytes = 0;
594 
595     if (PoolType == NonPagedPool)
596     {
597         /* Non pagable. Get PFN element corresponding to the PTE */
598         Pfn = MI_PFN_ELEMENT(PointerPte->u.Hard.PageFrameNumber);
599 
600         /* Count the page as free */
601         InterlockedDecrementUL(&MiSpecialPagesNonPaged);
602 
603         /* Lock PFN database */
604         Irql = MiAcquirePfnLock();
605 
606         /* Delete this PFN */
607         MI_SET_PFN_DELETED(Pfn);
608 
609         /* Decrement share count of this PFN */
610         MiDecrementShareCount(Pfn, PointerPte->u.Hard.PageFrameNumber);
611 
612         MI_ERASE_PTE(PointerPte);
613 
614         /* Flush the TLB */
615         //FIXME: Use KeFlushSingleTb() instead
616         KeFlushEntireTb(TRUE, TRUE);
617     }
618     else
619     {
620         /* Pagable. Delete that virtual address */
621         MiDeleteSystemPageableVm(PointerPte, 1, 0, NULL);
622 
623         /* Count the page as free */
624         InterlockedDecrementUL(&MiSpecialPagesPagable);
625 
626         /* Lock PFN database */
627         Irql = MiAcquirePfnLock();
628     }
629 
630     /* Mark next PTE as invalid */
631     MI_ERASE_PTE(PointerPte + 1);
632 
633     /* Make sure that the last entry is really the last one */
634     ASSERT(MiSpecialPoolLastPte->u.List.NextEntry == MM_EMPTY_PTE_LIST);
635 
636     /* Update the current last PTE next pointer */
637     MiSpecialPoolLastPte->u.List.NextEntry = PointerPte - MmSystemPteBase;
638 
639     /* PointerPte becomes the new last PTE */
640     PointerPte->u.List.NextEntry = MM_EMPTY_PTE_LIST;
641     MiSpecialPoolLastPte = PointerPte;
642 
643     /* Release the PFN database lock */
644     MiReleasePfnLock(Irql);
645 
646     /* Update page counter */
647     InterlockedDecrementUL(&MmSpecialPagesInUse);
648 }
649 
650 VOID
651 NTAPI
652 MiTestSpecialPool(VOID)
653 {
654     ULONG i;
655     PVOID p1, p2[100];
656     //PUCHAR p3;
657     ULONG ByteSize;
658     POOL_TYPE PoolType = PagedPool;
659 
660     // First allocate/free
661     for (i=0; i<100; i++)
662     {
663         ByteSize = (100 * (i+1)) % (PAGE_SIZE - sizeof(POOL_HEADER));
664         p1 = MmAllocateSpecialPool(ByteSize, 'TEST', PoolType, 0);
665         DPRINT1("p1 %p size %lu\n", p1, ByteSize);
666         MmFreeSpecialPool(p1);
667     }
668 
669     // Now allocate all at once, then free at once
670     for (i=0; i<100; i++)
671     {
672         ByteSize = (100 * (i+1)) % (PAGE_SIZE - sizeof(POOL_HEADER));
673         p2[i] = MmAllocateSpecialPool(ByteSize, 'TEST', PoolType, 0);
674         DPRINT1("p2[%lu] %p size %lu\n", i, p1, ByteSize);
675     }
676     for (i=0; i<100; i++)
677     {
678         DPRINT1("Freeing %p\n", p2[i]);
679         MmFreeSpecialPool(p2[i]);
680     }
681 
682     // Overrun the buffer to test
683     //ByteSize = 16;
684     //p3 = MmAllocateSpecialPool(ByteSize, 'TEST', NonPagedPool, 0);
685     //p3[ByteSize] = 0xF1; // This should cause an exception
686 
687     // Underrun the buffer to test
688     //p3 = MmAllocateSpecialPool(ByteSize, 'TEST', NonPagedPool, 1);
689     //p3--;
690     //*p3 = 0xF1; // This should cause an exception
691 
692 }
693 
694 /* EOF */
695