xref: /reactos/ntoskrnl/mm/ARM3/pfnlist.c (revision e5993f13)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/pfnlist.c
5  * PURPOSE:         ARM Memory Manager PFN List Manipulation
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
17 
18 #if DBG
19 #define ASSERT_LIST_INVARIANT(x) \
20 do { \
21     ASSERT(((x)->Total == 0 && \
22             (x)->Flink == LIST_HEAD && \
23             (x)->Blink == LIST_HEAD) || \
24            ((x)->Total != 0 && \
25             (x)->Flink != LIST_HEAD && \
26             (x)->Blink != LIST_HEAD)); \
27 } while (0)
28 #else
29 #define ASSERT_LIST_INVARIANT(x)
30 #endif
31 
32 /* GLOBALS ********************************************************************/
33 
34 BOOLEAN MmDynamicPfn;
35 BOOLEAN MmMirroring;
36 ULONG MmSystemPageColor;
37 
38 ULONG MmTransitionSharedPages;
39 ULONG MmTotalPagesForPagingFile;
40 
41 MMPFNLIST MmZeroedPageListHead = {0, ZeroedPageList, LIST_HEAD, LIST_HEAD};
42 MMPFNLIST MmFreePageListHead = {0, FreePageList, LIST_HEAD, LIST_HEAD};
43 MMPFNLIST MmStandbyPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
44 MMPFNLIST MmStandbyPageListByPriority[8];
45 MMPFNLIST MmModifiedPageListHead = {0, ModifiedPageList, LIST_HEAD, LIST_HEAD};
46 MMPFNLIST MmModifiedPageListByColor[1] = {{0, ModifiedPageList, LIST_HEAD, LIST_HEAD}};
47 MMPFNLIST MmModifiedNoWritePageListHead = {0, ModifiedNoWritePageList, LIST_HEAD, LIST_HEAD};
48 MMPFNLIST MmBadPageListHead = {0, BadPageList, LIST_HEAD, LIST_HEAD};
49 MMPFNLIST MmRomPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
50 
51 PMMPFNLIST MmPageLocationList[] =
52 {
53     &MmZeroedPageListHead,
54     &MmFreePageListHead,
55     &MmStandbyPageListHead,
56     &MmModifiedPageListHead,
57     &MmModifiedNoWritePageListHead,
58     &MmBadPageListHead,
59     NULL,
60     NULL
61 };
62 
63 ULONG MI_PFN_CURRENT_USAGE;
64 CHAR MI_PFN_CURRENT_PROCESS_NAME[16] = "None yet";
65 
66 /* FUNCTIONS ******************************************************************/
67 static
68 VOID
69 MiIncrementAvailablePages(
70     VOID)
71 {
72     /* Increment available pages */
73     MmAvailablePages++;
74 
75     /* Check if we've reached the configured low memory threshold */
76     if (MmAvailablePages == MmLowMemoryThreshold)
77     {
78         /* Clear the event, because now we're ABOVE the threshold */
79         KeClearEvent(MiLowMemoryEvent);
80     }
81     else if (MmAvailablePages == MmHighMemoryThreshold)
82     {
83         /* Otherwise check if we reached the high threshold and signal the event */
84         KeSetEvent(MiHighMemoryEvent, 0, FALSE);
85     }
86 }
87 
88 static
89 VOID
90 MiDecrementAvailablePages(
91     VOID)
92 {
93     ASSERT(MmAvailablePages > 0);
94 
95     /* See if we hit any thresholds */
96     if (MmAvailablePages == MmHighMemoryThreshold)
97     {
98         /* Clear the high memory event */
99         KeClearEvent(MiHighMemoryEvent);
100     }
101     else if (MmAvailablePages == MmLowMemoryThreshold)
102     {
103         /* Signal the low memory event */
104         KeSetEvent(MiLowMemoryEvent, 0, FALSE);
105     }
106 
107     /* One less page */
108     MmAvailablePages--;
109     if (MmAvailablePages < MmMinimumFreePages)
110     {
111         /* FIXME: Should wake up the MPW and working set manager, if we had one */
112 
113         DPRINT1("Running low on pages: %lu remaining\n", MmAvailablePages);
114 
115         /* Call RosMm and see if it can release any pages for us */
116         MmRebalanceMemoryConsumers();
117     }
118 }
119 
120 VOID
121 NTAPI
122 MiZeroPhysicalPage(IN PFN_NUMBER PageFrameIndex)
123 {
124     KIRQL OldIrql;
125     PVOID VirtualAddress;
126     PEPROCESS Process = PsGetCurrentProcess();
127 
128     /* Map in hyperspace, then wipe it using XMMI or MEMSET */
129     VirtualAddress = MiMapPageInHyperSpace(Process, PageFrameIndex, &OldIrql);
130     ASSERT(VirtualAddress);
131     KeZeroPages(VirtualAddress, PAGE_SIZE);
132     MiUnmapPageInHyperSpace(Process, VirtualAddress, OldIrql);
133 }
134 
135 VOID
136 NTAPI
137 MiUnlinkFreeOrZeroedPage(IN PMMPFN Entry)
138 {
139     PFN_NUMBER OldFlink, OldBlink;
140     PMMPFNLIST ListHead;
141     MMLISTS ListName;
142     ULONG Color;
143     PMMCOLOR_TABLES ColorTable;
144     PMMPFN Pfn1;
145 
146     /* Make sure the PFN lock is held */
147     MI_ASSERT_PFN_LOCK_HELD();
148 
149     /* Make sure the PFN entry isn't in-use */
150     ASSERT(Entry->u3.e1.WriteInProgress == 0);
151     ASSERT(Entry->u3.e1.ReadInProgress == 0);
152 
153     /* Find the list for this entry, make sure it's the free or zero list */
154     ListHead = MmPageLocationList[Entry->u3.e1.PageLocation];
155     ListName = ListHead->ListName;
156     ASSERT(ListHead != NULL);
157     ASSERT(ListName <= FreePageList);
158     ASSERT_LIST_INVARIANT(ListHead);
159 
160     /* Remove one count */
161     ASSERT(ListHead->Total != 0);
162     ListHead->Total--;
163 
164     /* Get the forward and back pointers */
165     OldFlink = Entry->u1.Flink;
166     OldBlink = Entry->u2.Blink;
167 
168     /* Check if the next entry is the list head */
169     if (OldFlink != LIST_HEAD)
170     {
171         /* It is not, so set the backlink of the actual entry, to our backlink */
172         MI_PFN_ELEMENT(OldFlink)->u2.Blink = OldBlink;
173     }
174     else
175     {
176         /* Set the list head's backlink instead */
177         ListHead->Blink = OldBlink;
178     }
179 
180     /* Check if the back entry is the list head */
181     if (OldBlink != LIST_HEAD)
182     {
183         /* It is not, so set the backlink of the actual entry, to our backlink */
184         MI_PFN_ELEMENT(OldBlink)->u1.Flink = OldFlink;
185     }
186     else
187     {
188         /* Set the list head's backlink instead */
189         ListHead->Flink = OldFlink;
190     }
191 
192     /* Get the page color */
193     OldBlink = MiGetPfnEntryIndex(Entry);
194     Color = OldBlink & MmSecondaryColorMask;
195 
196     /* Get the first page on the color list */
197     ColorTable = &MmFreePagesByColor[ListName][Color];
198 
199     /* Check if this was was actually the head */
200     OldFlink = ColorTable->Flink;
201     if (OldFlink == OldBlink)
202     {
203         /* Make the table point to the next page this page was linking to */
204         ColorTable->Flink = Entry->OriginalPte.u.Long;
205         if (ColorTable->Flink != LIST_HEAD)
206         {
207             /* And make the previous link point to the head now */
208             MI_PFN_ELEMENT(ColorTable->Flink)->u4.PteFrame = COLORED_LIST_HEAD;
209         }
210         else
211         {
212             /* And if that page was the head, loop the list back around */
213             ColorTable->Blink = (PVOID)LIST_HEAD;
214         }
215     }
216     else
217     {
218         /* This page shouldn't be pointing back to the head */
219         ASSERT(Entry->u4.PteFrame != COLORED_LIST_HEAD);
220 
221         /* Make the back link point to whoever the next page is */
222         Pfn1 = MI_PFN_ELEMENT(Entry->u4.PteFrame);
223         Pfn1->OriginalPte.u.Long = Entry->OriginalPte.u.Long;
224 
225         /* Check if this page was pointing to the head */
226         if (Entry->OriginalPte.u.Long != LIST_HEAD)
227         {
228             /* Make the back link point to the head */
229             Pfn1 = MI_PFN_ELEMENT(Entry->OriginalPte.u.Long);
230             Pfn1->u4.PteFrame = Entry->u4.PteFrame;
231         }
232         else
233         {
234             /* Then the table is directly back pointing to this page now */
235             ColorTable->Blink = Pfn1;
236         }
237     }
238 
239     /* One less colored page */
240     ASSERT(ColorTable->Count >= 1);
241     ColorTable->Count--;
242 
243     /* ReactOS Hack */
244     Entry->OriginalPte.u.Long = 0;
245 
246     /* We are not on a list anymore */
247     Entry->u1.Flink = Entry->u2.Blink = 0;
248     ASSERT_LIST_INVARIANT(ListHead);
249 
250     /* Decrement number of available pages */
251     MiDecrementAvailablePages();
252 
253 #if MI_TRACE_PFNS
254     ASSERT(MI_PFN_CURRENT_USAGE != MI_USAGE_NOT_SET);
255     Entry->PfnUsage = MI_PFN_CURRENT_USAGE;
256     memcpy(Entry->ProcessName, MI_PFN_CURRENT_PROCESS_NAME, 16);
257     Entry->CallSite = _ReturnAddress();
258     MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
259     MI_SET_PROCESS2("Not Set");
260 #endif
261 }
262 
263 VOID
264 NTAPI
265 MiUnlinkPageFromList(IN PMMPFN Pfn)
266 {
267     PMMPFNLIST ListHead;
268     PFN_NUMBER OldFlink, OldBlink;
269 
270     /* Make sure the PFN lock is held */
271     MI_ASSERT_PFN_LOCK_HELD();
272 
273     /* ARM3 should only call this for dead pages */
274     ASSERT(Pfn->u3.e2.ReferenceCount == 0);
275 
276     /* Transition pages are supposed to be standby/modified/nowrite */
277     ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation];
278     ASSERT(ListHead->ListName >= StandbyPageList);
279 
280     /* Check if this was standby, or modified */
281     if (ListHead == &MmStandbyPageListHead)
282     {
283         /* Should not be a ROM page */
284         ASSERT(Pfn->u3.e1.Rom == 0);
285 
286         /* Get the exact list */
287         ListHead = &MmStandbyPageListByPriority[Pfn->u4.Priority];
288 
289         /* Decrement number of available pages */
290         MiDecrementAvailablePages();
291 
292         /* Decrease transition page counter */
293         ASSERT(Pfn->u3.e1.PrototypePte == 1); /* Only supported ARM3 case */
294         MmTransitionSharedPages--;
295     }
296     else if (ListHead == &MmModifiedPageListHead)
297     {
298         /* Only shared memory (page-file backed) modified pages are supported */
299         ASSERT(Pfn->OriginalPte.u.Soft.Prototype == 0);
300 
301         /* Decrement the counters */
302         ListHead->Total--;
303         MmTotalPagesForPagingFile--;
304 
305         /* Pick the correct colored list */
306         ListHead = &MmModifiedPageListByColor[0];
307 
308         /* Decrease transition page counter */
309         MmTransitionSharedPages--;
310     }
311     else if (ListHead == &MmModifiedNoWritePageListHead)
312     {
313         /* List not yet supported */
314         ASSERT(FALSE);
315     }
316 
317     /* Nothing should be in progress and the list should not be empty */
318     ASSERT(Pfn->u3.e1.WriteInProgress == 0);
319     ASSERT(Pfn->u3.e1.ReadInProgress == 0);
320     ASSERT(ListHead->Total != 0);
321 
322     /* Get the forward and back pointers */
323     OldFlink = Pfn->u1.Flink;
324     OldBlink = Pfn->u2.Blink;
325 
326     /* Check if the next entry is the list head */
327     if (OldFlink != LIST_HEAD)
328     {
329         /* It is not, so set the backlink of the actual entry, to our backlink */
330         MI_PFN_ELEMENT(OldFlink)->u2.Blink = OldBlink;
331     }
332     else
333     {
334         /* Set the list head's backlink instead */
335         ListHead->Blink = OldBlink;
336     }
337 
338     /* Check if the back entry is the list head */
339     if (OldBlink != LIST_HEAD)
340     {
341         /* It is not, so set the backlink of the actual entry, to our backlink */
342         MI_PFN_ELEMENT(OldBlink)->u1.Flink = OldFlink;
343     }
344     else
345     {
346         /* Set the list head's backlink instead */
347         ListHead->Flink = OldFlink;
348     }
349 
350     /* ReactOS Hack */
351     Pfn->OriginalPte.u.Long = 0;
352 
353     /* We are not on a list anymore */
354     Pfn->u1.Flink = Pfn->u2.Blink = 0;
355 
356     /* Remove one entry from the list */
357     ListHead->Total--;
358 
359     ASSERT_LIST_INVARIANT(ListHead);
360 }
361 
362 PFN_NUMBER
363 NTAPI
364 MiRemovePageByColor(IN PFN_NUMBER PageIndex,
365                     IN ULONG Color)
366 {
367     PMMPFN Pfn1;
368     PMMPFNLIST ListHead;
369     MMLISTS ListName;
370     PFN_NUMBER OldFlink, OldBlink;
371     USHORT OldColor, OldCache;
372     PMMCOLOR_TABLES ColorTable;
373 
374     /* Make sure PFN lock is held */
375     MI_ASSERT_PFN_LOCK_HELD();
376     ASSERT(Color < MmSecondaryColors);
377 
378     /* Get the PFN entry */
379     Pfn1 = MI_PFN_ELEMENT(PageIndex);
380     ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
381     ASSERT(Pfn1->u3.e1.Rom == 0);
382 
383     /* Capture data for later */
384     OldColor = Pfn1->u3.e1.PageColor;
385     OldCache = Pfn1->u3.e1.CacheAttribute;
386 
387     /* Could be either on free or zero list */
388     ListHead = MmPageLocationList[Pfn1->u3.e1.PageLocation];
389     ASSERT_LIST_INVARIANT(ListHead);
390     ListName = ListHead->ListName;
391     ASSERT(ListName <= FreePageList);
392 
393     /* Remove a page */
394     ListHead->Total--;
395 
396     /* Get the forward and back pointers */
397     OldFlink = Pfn1->u1.Flink;
398     OldBlink = Pfn1->u2.Blink;
399 
400     /* Check if the next entry is the list head */
401     if (OldFlink != LIST_HEAD)
402     {
403         /* It is not, so set the backlink of the actual entry, to our backlink */
404         MI_PFN_ELEMENT(OldFlink)->u2.Blink = OldBlink;
405     }
406     else
407     {
408         /* Set the list head's backlink instead */
409         ListHead->Blink = OldBlink;
410     }
411 
412     /* Check if the back entry is the list head */
413     if (OldBlink != LIST_HEAD)
414     {
415         /* It is not, so set the backlink of the actual entry, to our backlink */
416         MI_PFN_ELEMENT(OldBlink)->u1.Flink = OldFlink;
417     }
418     else
419     {
420         /* Set the list head's backlink instead */
421         ListHead->Flink = OldFlink;
422     }
423 
424     /* We are not on a list anymore */
425     ASSERT_LIST_INVARIANT(ListHead);
426     Pfn1->u1.Flink = Pfn1->u2.Blink = 0;
427 
428     /* Zero flags but restore color and cache */
429     Pfn1->u3.e2.ShortFlags = 0;
430     Pfn1->u3.e1.PageColor = OldColor;
431     Pfn1->u3.e1.CacheAttribute = OldCache;
432 
433     /* Get the first page on the color list */
434     ASSERT(Color < MmSecondaryColors);
435     ColorTable = &MmFreePagesByColor[ListName][Color];
436     ASSERT(ColorTable->Count >= 1);
437 
438     /* Set the forward link to whoever we were pointing to */
439     ColorTable->Flink = Pfn1->OriginalPte.u.Long;
440 
441     /* Get the first page on the color list */
442     if (ColorTable->Flink == LIST_HEAD)
443     {
444         /* This is the beginning of the list, so set the sentinel value */
445         ColorTable->Blink = (PVOID)LIST_HEAD;
446     }
447     else
448     {
449         /* The list is empty, so we are the first page */
450         MI_PFN_ELEMENT(ColorTable->Flink)->u4.PteFrame = COLORED_LIST_HEAD;
451     }
452 
453     /* One less page */
454     ColorTable->Count--;
455 
456     /* ReactOS Hack */
457     Pfn1->OriginalPte.u.Long = 0;
458 
459     /* Decrement number of available pages */
460     MiDecrementAvailablePages();
461 
462 #if MI_TRACE_PFNS
463     ASSERT(MI_PFN_CURRENT_USAGE != MI_USAGE_NOT_SET);
464     Pfn1->PfnUsage = MI_PFN_CURRENT_USAGE;
465     memcpy(Pfn1->ProcessName, MI_PFN_CURRENT_PROCESS_NAME, 16);
466     Pfn1->CallSite = _ReturnAddress();
467     MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET;
468     MI_SET_PROCESS2("Not Set");
469 #endif
470 
471     /* Return the page */
472     return PageIndex;
473 }
474 
475 PFN_NUMBER
476 NTAPI
477 MiRemoveAnyPage(IN ULONG Color)
478 {
479     PFN_NUMBER PageIndex;
480     PMMPFN Pfn1;
481 
482     /* Make sure PFN lock is held and we have pages */
483     MI_ASSERT_PFN_LOCK_HELD();
484     ASSERT(Color < MmSecondaryColors);
485     if (MmAvailablePages == 0)
486     {
487         return 0;
488     }
489 
490     /* Check the colored free list */
491     PageIndex = MmFreePagesByColor[FreePageList][Color].Flink;
492     if (PageIndex == LIST_HEAD)
493     {
494         /* Check the colored zero list */
495         PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
496         if (PageIndex == LIST_HEAD)
497         {
498             /* Check the free list */
499             ASSERT_LIST_INVARIANT(&MmFreePageListHead);
500             PageIndex = MmFreePageListHead.Flink;
501             Color = PageIndex & MmSecondaryColorMask;
502             if (PageIndex == LIST_HEAD)
503             {
504                 /* Check the zero list */
505                 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
506                 PageIndex = MmZeroedPageListHead.Flink;
507                 Color = PageIndex & MmSecondaryColorMask;
508                 ASSERT(PageIndex != LIST_HEAD);
509                 if (PageIndex == LIST_HEAD)
510                 {
511                     /* FIXME: Should check the standby list */
512                     ASSERT(MmZeroedPageListHead.Total == 0);
513                 }
514             }
515         }
516     }
517 
518     /* Remove the page from its list */
519     PageIndex = MiRemovePageByColor(PageIndex, Color);
520     ASSERT(PageIndex != 0);
521 
522     /* Sanity checks */
523     Pfn1 = MI_PFN_ELEMENT(PageIndex);
524     ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) ||
525            (Pfn1->u3.e1.PageLocation == ZeroedPageList));
526     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
527     ASSERT(Pfn1->u2.ShareCount == 0);
528     ASSERT_LIST_INVARIANT(&MmFreePageListHead);
529     ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
530 
531     /* Return the page */
532     return PageIndex;
533 }
534 
535 PFN_NUMBER
536 NTAPI
537 MiRemoveZeroPage(IN ULONG Color)
538 {
539     PFN_NUMBER PageIndex;
540     PMMPFN Pfn1;
541     BOOLEAN Zero = FALSE;
542 
543     /* Make sure PFN lock is held and we have pages */
544     MI_ASSERT_PFN_LOCK_HELD();
545     ASSERT(Color < MmSecondaryColors);
546     if (MmAvailablePages == 0)
547     {
548         return 0;
549     }
550 
551     /* Check the colored zero list */
552     PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
553     if (PageIndex == LIST_HEAD)
554     {
555         /* Check the zero list */
556         ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
557         PageIndex = MmZeroedPageListHead.Flink;
558         if (PageIndex == LIST_HEAD)
559         {
560             /* This means there's no zero pages, we have to look for free ones */
561             ASSERT(MmZeroedPageListHead.Total == 0);
562             Zero = TRUE;
563 
564             /* Check the colored free list */
565             PageIndex = MmFreePagesByColor[FreePageList][Color].Flink;
566             if (PageIndex == LIST_HEAD)
567             {
568                 /* Check the free list */
569                 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
570                 PageIndex = MmFreePageListHead.Flink;
571                 Color = PageIndex & MmSecondaryColorMask;
572                 ASSERT(PageIndex != LIST_HEAD);
573                 if (PageIndex == LIST_HEAD)
574                 {
575                     /* FIXME: Should check the standby list */
576                     ASSERT(MmZeroedPageListHead.Total == 0);
577                 }
578             }
579         }
580         else
581         {
582             Color = PageIndex & MmSecondaryColorMask;
583         }
584     }
585 
586     /* Sanity checks */
587     Pfn1 = MI_PFN_ELEMENT(PageIndex);
588     ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) ||
589            (Pfn1->u3.e1.PageLocation == ZeroedPageList));
590 
591     /* Remove the page from its list */
592     PageIndex = MiRemovePageByColor(PageIndex, Color);
593     ASSERT(PageIndex != 0);
594     ASSERT(Pfn1 == MI_PFN_ELEMENT(PageIndex));
595 
596     /* Zero it, if needed */
597     if (Zero) MiZeroPhysicalPage(PageIndex);
598 
599     /* Sanity checks */
600     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
601     ASSERT(Pfn1->u2.ShareCount == 0);
602     ASSERT_LIST_INVARIANT(&MmFreePageListHead);
603     ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
604 
605     /* Return the page */
606     return PageIndex;
607 }
608 
609 VOID
610 NTAPI
611 MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex)
612 {
613     PMMPFNLIST ListHead;
614     PFN_NUMBER LastPage;
615     PMMPFN Pfn1;
616     ULONG Color;
617     PMMPFN Blink;
618     PMMCOLOR_TABLES ColorTable;
619 
620     /* Make sure the page index is valid */
621     MI_ASSERT_PFN_LOCK_HELD();
622     ASSERT((PageFrameIndex != 0) &&
623            (PageFrameIndex <= MmHighestPhysicalPage) &&
624            (PageFrameIndex >= MmLowestPhysicalPage));
625 
626     /* Get the PFN entry */
627     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
628 
629     /* Sanity checks that a right kind of page is being inserted here */
630     ASSERT(Pfn1->u4.MustBeCached == 0);
631     ASSERT(Pfn1->u3.e1.Rom != 1);
632     ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
633     ASSERT(Pfn1->u4.VerifierAllocation == 0);
634     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
635 
636     /* Get the free page list and increment its count */
637     ListHead = &MmFreePageListHead;
638     ASSERT_LIST_INVARIANT(ListHead);
639     ListHead->Total++;
640 
641     /* Get the last page on the list */
642     LastPage = ListHead->Blink;
643     if (LastPage != LIST_HEAD)
644     {
645         /* Link us with the previous page, so we're at the end now */
646         MI_PFN_ELEMENT(LastPage)->u1.Flink = PageFrameIndex;
647     }
648     else
649     {
650         /* The list is empty, so we are the first page */
651         ListHead->Flink = PageFrameIndex;
652     }
653 
654     /* Now make the list head point back to us (since we go at the end) */
655     ListHead->Blink = PageFrameIndex;
656     ASSERT_LIST_INVARIANT(ListHead);
657 
658     /* And initialize our own list pointers */
659     Pfn1->u1.Flink = LIST_HEAD;
660     Pfn1->u2.Blink = LastPage;
661 
662     /* Set the list name and default priority */
663     Pfn1->u3.e1.PageLocation = FreePageList;
664     Pfn1->u4.Priority = 3;
665 
666     /* Clear some status fields */
667     Pfn1->u4.InPageError = 0;
668     Pfn1->u4.AweAllocation = 0;
669 
670     /* Increment number of available pages */
671     MiIncrementAvailablePages();
672 
673     /* Get the page color */
674     Color = PageFrameIndex & MmSecondaryColorMask;
675 
676     /* Get the first page on the color list */
677     ColorTable = &MmFreePagesByColor[FreePageList][Color];
678     if (ColorTable->Flink == LIST_HEAD)
679     {
680         /* The list is empty, so we are the first page */
681         Pfn1->u4.PteFrame = COLORED_LIST_HEAD;
682         ColorTable->Flink = PageFrameIndex;
683     }
684     else
685     {
686         /* Get the previous page */
687         Blink = (PMMPFN)ColorTable->Blink;
688 
689         /* Make it link to us, and link back to it */
690         Blink->OriginalPte.u.Long = PageFrameIndex;
691         Pfn1->u4.PteFrame = MiGetPfnEntryIndex(Blink);
692     }
693 
694     /* Now initialize our own list pointers */
695     ColorTable->Blink = Pfn1;
696 
697     /* This page is now the last */
698     Pfn1->OriginalPte.u.Long = LIST_HEAD;
699 
700     /* And increase the count in the colored list */
701     ColorTable->Count++;
702 
703     /* Notify zero page thread if enough pages are on the free list now */
704     if (ListHead->Total >= 8)
705     {
706         /* Set the event */
707         KeSetEvent(&MmZeroingPageEvent, IO_NO_INCREMENT, FALSE);
708     }
709 
710 #if MI_TRACE_PFNS
711     Pfn1->PfnUsage = MI_USAGE_FREE_PAGE;
712     RtlZeroMemory(Pfn1->ProcessName, 16);
713     Pfn1->CallSite = NULL;
714 #endif
715 }
716 
717 VOID
718 FASTCALL
719 MiInsertStandbyListAtFront(IN PFN_NUMBER PageFrameIndex)
720 {
721     PMMPFNLIST ListHead;
722     PFN_NUMBER Flink;
723     PMMPFN Pfn1, Pfn2;
724 
725     /* Make sure the lock is held */
726     DPRINT("Inserting page: %lx into standby list !\n", PageFrameIndex);
727     MI_ASSERT_PFN_LOCK_HELD();
728 
729     /* Make sure the PFN is valid */
730     ASSERT((PageFrameIndex != 0) &&
731            (PageFrameIndex <= MmHighestPhysicalPage) &&
732            (PageFrameIndex >= MmLowestPhysicalPage));
733 
734     /* Grab the PFN and validate it is the right kind of PFN being inserted */
735     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
736     ASSERT(Pfn1->u4.MustBeCached == 0);
737     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
738     ASSERT(Pfn1->u3.e1.PrototypePte == 1);
739     ASSERT(Pfn1->u3.e1.Rom != 1);
740 
741     /* One more transition page on a list */
742     MmTransitionSharedPages++;
743 
744     /* Get the standby page list and increment its count */
745     ListHead = &MmStandbyPageListByPriority [Pfn1->u4.Priority];
746     ASSERT_LIST_INVARIANT(ListHead);
747     ListHead->Total++;
748 
749     /* Make the head of the list point to this page now */
750     Flink = ListHead->Flink;
751     ListHead->Flink = PageFrameIndex;
752 
753     /* Make the page point to the previous head, and back to the list */
754     Pfn1->u1.Flink = Flink;
755     Pfn1->u2.Blink = LIST_HEAD;
756 
757     /* Was the list empty? */
758     if (Flink != LIST_HEAD)
759     {
760         /* It wasn't, so update the backlink of the previous head page */
761         Pfn2 = MI_PFN_ELEMENT(Flink);
762         Pfn2->u2.Blink = PageFrameIndex;
763     }
764     else
765     {
766         /* It was empty, so have it loop back around to this new page */
767         ListHead->Blink = PageFrameIndex;
768     }
769 
770     /* Move the page onto its new location */
771     Pfn1->u3.e1.PageLocation = StandbyPageList;
772 
773     /* Increment number of available pages */
774     MiIncrementAvailablePages();
775 }
776 
777 VOID
778 NTAPI
779 MiInsertPageInList(IN PMMPFNLIST ListHead,
780                    IN PFN_NUMBER PageFrameIndex)
781 {
782     PFN_NUMBER Flink, LastPage;
783     PMMPFN Pfn1, Pfn2;
784     MMLISTS ListName;
785     PMMCOLOR_TABLES ColorHead;
786     ULONG Color;
787 
788     /* For free pages, use MiInsertPageInFreeList */
789     ASSERT(ListHead != &MmFreePageListHead);
790 
791     /* Make sure the lock is held */
792     MI_ASSERT_PFN_LOCK_HELD();
793 
794     /* Make sure the PFN is valid */
795     ASSERT((PageFrameIndex) &&
796            (PageFrameIndex <= MmHighestPhysicalPage) &&
797            (PageFrameIndex >= MmLowestPhysicalPage));
798 
799     /* Page should be unused */
800     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
801     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
802     ASSERT(Pfn1->u3.e1.Rom != 1);
803 
804     /* Is a standby or modified page being inserted? */
805     ListName = ListHead->ListName;
806     if ((ListName == StandbyPageList) || (ListName == ModifiedPageList))
807     {
808         /* If the page is in transition, it must also be a prototype page */
809         if ((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
810             (Pfn1->OriginalPte.u.Soft.Transition == 1))
811         {
812             /* Crash the system on inconsistency */
813             KeBugCheckEx(MEMORY_MANAGEMENT, 0x8888, 0, 0, 0);
814         }
815     }
816 
817     /* Standby pages are prioritized, so we need to get the real head */
818     if (ListHead == &MmStandbyPageListHead)
819     {
820         /* Obviously the prioritized list should still have the same name */
821         ListHead = &MmStandbyPageListByPriority[Pfn1->u4.Priority];
822         ASSERT(ListHead->ListName == ListName);
823     }
824 
825     /* Increment the list count */
826     ListHead->Total++;
827 
828     /* Is a modified page being inserted? */
829     if (ListHead == &MmModifiedPageListHead)
830     {
831         /* For now, only single-prototype pages should end up in this path */
832         DPRINT("Modified page being added: %lx\n", PageFrameIndex);
833         ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0);
834 
835         /* Modified pages are colored when they are selected for page file */
836         ListHead = &MmModifiedPageListByColor[0];
837         ASSERT (ListHead->ListName == ListName);
838         ListHead->Total++;
839 
840         /* Increment the number of paging file modified pages */
841         MmTotalPagesForPagingFile++;
842     }
843 
844     /* Don't handle bad pages yet yet */
845     ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
846 
847     /* Zero pages go to the head, all other pages go to the end */
848     if (ListName == ZeroedPageList)
849     {
850         /* Make the head of the list point to this page now */
851         Flink = ListHead->Flink;
852         ListHead->Flink = PageFrameIndex;
853 
854         /* Make the page point to the previous head, and back to the list */
855         Pfn1->u1.Flink = Flink;
856         Pfn1->u2.Blink = LIST_HEAD;
857 
858         /* Was the list empty? */
859         if (Flink != LIST_HEAD)
860         {
861             /* It wasn't, so update the backlink of the previous head page */
862             Pfn2 = MI_PFN_ELEMENT(Flink);
863             Pfn2->u2.Blink = PageFrameIndex;
864         }
865         else
866         {
867             /* It was empty, so have it loop back around to this new page */
868             ListHead->Blink = PageFrameIndex;
869         }
870     }
871     else
872     {
873         /* Get the last page on the list */
874         LastPage = ListHead->Blink;
875         if (LastPage != LIST_HEAD)
876         {
877             /* Link us with the previous page, so we're at the end now */
878             MI_PFN_ELEMENT(LastPage)->u1.Flink = PageFrameIndex;
879         }
880         else
881         {
882             /* The list is empty, so we are the first page */
883             ListHead->Flink = PageFrameIndex;
884         }
885 
886         /* Now make the list head point back to us (since we go at the end) */
887         ListHead->Blink = PageFrameIndex;
888         ASSERT_LIST_INVARIANT(ListHead);
889 
890         /* And initialize our own list pointers */
891         Pfn1->u1.Flink = LIST_HEAD;
892         Pfn1->u2.Blink = LastPage;
893     }
894 
895     /* Move the page onto its new location */
896     Pfn1->u3.e1.PageLocation = ListName;
897 
898     /* For zero/free pages, we also have to handle the colored lists */
899     if (ListName <= StandbyPageList)
900     {
901         /* Increment number of available pages */
902         MiIncrementAvailablePages();
903 
904         /* Sanity checks */
905         ASSERT(ListName == ZeroedPageList);
906         ASSERT(Pfn1->u4.InPageError == 0);
907 
908         /* Get the page color */
909         Color = PageFrameIndex & MmSecondaryColorMask;
910 
911         /* Get the list for this color */
912         ColorHead = &MmFreePagesByColor[ZeroedPageList][Color];
913 
914         /* Get the old head */
915         Flink = ColorHead->Flink;
916 
917         /* Make this page point back to the list, and point forwards to the old head */
918         Pfn1->OriginalPte.u.Long = Flink;
919         Pfn1->u4.PteFrame = COLORED_LIST_HEAD;
920 
921         /* Set the new head */
922         ColorHead->Flink = PageFrameIndex;
923 
924         /* Was the head empty? */
925         if (Flink != LIST_HEAD)
926         {
927             /* No, so make the old head point to this page */
928             Pfn2 = MI_PFN_ELEMENT(Flink);
929             Pfn2->u4.PteFrame = PageFrameIndex;
930         }
931         else
932         {
933             /* Yes, make it loop back to this page */
934             ColorHead->Blink = (PVOID)Pfn1;
935         }
936 
937         /* One more paged on the colored list */
938         ColorHead->Count++;
939 
940 #if MI_TRACE_PFNS
941             ASSERT(MI_PFN_CURRENT_USAGE == MI_USAGE_NOT_SET);
942             Pfn1->PfnUsage = MI_USAGE_FREE_PAGE;
943             RtlZeroMemory(Pfn1->ProcessName, 16);
944             Pfn1->CallSite = NULL;
945 #endif
946     }
947     else if (ListName == ModifiedPageList)
948     {
949         /* In ARM3, page must be destined for page file, and not yet written out */
950         ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0);
951         ASSERT(Pfn1->OriginalPte.u.Soft.PageFileHigh == 0);
952 
953         /* One more transition page */
954         MmTransitionSharedPages++;
955 
956         /* Increment the number of per-process modified pages */
957         PsGetCurrentProcess()->ModifiedPageCount++;
958 
959         /* FIXME: Wake up modified page writer if there are not enough free pages */
960     }
961     else if (ListName == ModifiedNoWritePageList)
962     {
963         /* This list is not yet implemented */
964         ASSERT(FALSE);
965     }
966 }
967 
968 VOID
969 NTAPI
970 MiInitializePfn(IN PFN_NUMBER PageFrameIndex,
971                 IN PMMPTE PointerPte,
972                 IN BOOLEAN Modified)
973 {
974     PMMPFN Pfn1;
975     NTSTATUS Status;
976     PMMPTE PointerPtePte;
977     MI_ASSERT_PFN_LOCK_HELD();
978 
979     /* Setup the PTE */
980     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
981     Pfn1->PteAddress = PointerPte;
982 
983     DPRINT("Called for %p from %p\n", Pfn1, _ReturnAddress());
984 
985     /* Check if this PFN is part of a valid address space */
986     if (PointerPte->u.Hard.Valid == 1)
987     {
988         /* Only valid from MmCreateProcessAddressSpace path */
989         ASSERT(PsGetCurrentProcess()->Vm.WorkingSetSize == 0);
990 
991         /* Make this a demand zero PTE */
992         MI_MAKE_SOFTWARE_PTE(&Pfn1->OriginalPte, MM_READWRITE);
993     }
994     else
995     {
996         /* Copy the PTE data */
997         Pfn1->OriginalPte = *PointerPte;
998         ASSERT(!((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
999                  (Pfn1->OriginalPte.u.Soft.Transition == 1)));
1000     }
1001 
1002     /* Otherwise this is a fresh page -- set it up */
1003     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
1004     Pfn1->u3.e2.ReferenceCount = 1;
1005     Pfn1->u2.ShareCount = 1;
1006     Pfn1->u3.e1.PageLocation = ActiveAndValid;
1007     ASSERT(Pfn1->u3.e1.Rom == 0);
1008     Pfn1->u3.e1.Modified = Modified;
1009 
1010     /* Get the page table for the PTE */
1011     PointerPtePte = MiAddressToPte(PointerPte);
1012     if (PointerPtePte->u.Hard.Valid == 0)
1013     {
1014         /* Make sure the PDE gets paged in properly */
1015         Status = MiCheckPdeForPagedPool(PointerPte);
1016         if (!NT_SUCCESS(Status))
1017         {
1018             /* Crash */
1019             KeBugCheckEx(MEMORY_MANAGEMENT,
1020                          0x61940,
1021                          (ULONG_PTR)PointerPte,
1022                          (ULONG_PTR)PointerPtePte->u.Long,
1023                          (ULONG_PTR)MiPteToAddress(PointerPte));
1024         }
1025     }
1026 
1027     /* Get the PFN for the page table */
1028     PageFrameIndex = PFN_FROM_PTE(PointerPtePte);
1029     ASSERT(PageFrameIndex != 0);
1030     Pfn1->u4.PteFrame = PageFrameIndex;
1031 
1032     DPRINT("Incrementing share count of %lp from %p\n", PageFrameIndex, _ReturnAddress());
1033 
1034     /* Increase its share count so we don't get rid of it */
1035     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1036     Pfn1->u2.ShareCount++;
1037 }
1038 
1039 VOID
1040 NTAPI
1041 MiInitializePfnAndMakePteValid(IN PFN_NUMBER PageFrameIndex,
1042                                IN PMMPTE PointerPte,
1043                                IN MMPTE TempPte)
1044 {
1045     PMMPFN Pfn1;
1046     NTSTATUS Status;
1047     PMMPTE PointerPtePte;
1048     MI_ASSERT_PFN_LOCK_HELD();
1049 
1050     /* PTE must be invalid */
1051     ASSERT(PointerPte->u.Hard.Valid == 0);
1052 
1053     /* Setup the PTE */
1054     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1055     Pfn1->PteAddress = PointerPte;
1056     Pfn1->OriginalPte = DemandZeroPte;
1057 
1058     DPRINT("Incrementing %p from %p\n", Pfn1, _ReturnAddress());
1059 
1060     /* Otherwise this is a fresh page -- set it up */
1061     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
1062     Pfn1->u3.e2.ReferenceCount++;
1063     Pfn1->u2.ShareCount++;
1064     Pfn1->u3.e1.PageLocation = ActiveAndValid;
1065     ASSERT(Pfn1->u3.e1.Rom == 0);
1066     Pfn1->u3.e1.Modified = 1;
1067 
1068     /* Get the page table for the PTE */
1069     PointerPtePte = MiAddressToPte(PointerPte);
1070     if (PointerPtePte->u.Hard.Valid == 0)
1071     {
1072         /* Make sure the PDE gets paged in properly */
1073         Status = MiCheckPdeForPagedPool(PointerPte);
1074         if (!NT_SUCCESS(Status))
1075         {
1076             /* Crash */
1077             KeBugCheckEx(MEMORY_MANAGEMENT,
1078                          0x61940,
1079                          (ULONG_PTR)PointerPte,
1080                          (ULONG_PTR)PointerPtePte->u.Long,
1081                          (ULONG_PTR)MiPteToAddress(PointerPte));
1082         }
1083     }
1084 
1085     /* Get the PFN for the page table */
1086     PageFrameIndex = PFN_FROM_PTE(PointerPtePte);
1087     ASSERT(PageFrameIndex != 0);
1088     Pfn1->u4.PteFrame = PageFrameIndex;
1089 
1090     /* Increase its share count so we don't get rid of it */
1091     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1092     Pfn1->u2.ShareCount++;
1093     DPRINT("Incrementing %p from %p\n", Pfn1, _ReturnAddress());
1094 
1095     /* Write valid PTE */
1096     MI_WRITE_VALID_PTE(PointerPte, TempPte);
1097 }
1098 
1099 NTSTATUS
1100 NTAPI
1101 MiInitializeAndChargePfn(OUT PPFN_NUMBER PageFrameIndex,
1102                          IN PMMPDE PointerPde,
1103                          IN PFN_NUMBER ContainingPageFrame,
1104                          IN BOOLEAN SessionAllocation)
1105 {
1106     MMPDE TempPde;
1107     KIRQL OldIrql;
1108 
1109     /* Use either a global or local PDE */
1110     TempPde = SessionAllocation ? ValidKernelPdeLocal : ValidKernelPde;
1111 
1112     /* Lock the PFN database */
1113     OldIrql = MiAcquirePfnLock();
1114 
1115     /* Make sure nobody is racing us */
1116     if (PointerPde->u.Hard.Valid == 1)
1117     {
1118         /* Return special error if that was the case */
1119         MiReleasePfnLock(OldIrql);
1120         return STATUS_RETRY;
1121     }
1122 
1123     /* Grab a zero page and set the PFN, then make it valid */
1124     *PageFrameIndex = MiRemoveZeroPage(MI_GET_NEXT_COLOR());
1125     TempPde.u.Hard.PageFrameNumber = *PageFrameIndex;
1126     MI_WRITE_VALID_PDE(PointerPde, TempPde);
1127 
1128     /* Initialize the PFN */
1129     MiInitializePfnForOtherProcess(*PageFrameIndex,
1130                                    PointerPde,
1131                                    ContainingPageFrame);
1132     ASSERT(MI_PFN_ELEMENT(*PageFrameIndex)->u1.WsIndex == 0);
1133 
1134     /* Release the lock and return success */
1135     MiReleasePfnLock(OldIrql);
1136     return STATUS_SUCCESS;
1137 }
1138 
1139 VOID
1140 NTAPI
1141 MiDecrementShareCount(IN PMMPFN Pfn1,
1142                       IN PFN_NUMBER PageFrameIndex)
1143 {
1144     PMMPTE PointerPte;
1145     MMPTE TempPte;
1146 
1147     ASSERT(PageFrameIndex > 0);
1148     ASSERT(MI_PFN_ELEMENT(PageFrameIndex) != NULL);
1149     ASSERT(Pfn1 == MI_PFN_ELEMENT(PageFrameIndex));
1150     ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE);
1151 
1152     DPRINT("Decrementing %p from %p\n", Pfn1, _ReturnAddress());
1153 
1154     /* Page must be in-use */
1155     if ((Pfn1->u3.e1.PageLocation != ActiveAndValid) &&
1156         (Pfn1->u3.e1.PageLocation != StandbyPageList))
1157     {
1158         /* Otherwise we have PFN corruption */
1159         KeBugCheckEx(PFN_LIST_CORRUPT,
1160                      0x99,
1161                      PageFrameIndex,
1162                      Pfn1->u3.e1.PageLocation,
1163                      0);
1164     }
1165 
1166     /* Page should at least have one reference */
1167     ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
1168 
1169     /* Check if the share count is now 0 */
1170     ASSERT(Pfn1->u2.ShareCount < 0xF000000);
1171     if (!--Pfn1->u2.ShareCount)
1172     {
1173         /* Was this a prototype PTE? */
1174         if (Pfn1->u3.e1.PrototypePte)
1175         {
1176             /* Grab the PTE address and make sure it's in prototype pool */
1177             PointerPte = Pfn1->PteAddress;
1178             ASSERT((PointerPte >= (PMMPTE)MmPagedPoolStart) && (PointerPte <= (PMMPTE)MmPagedPoolEnd));
1179 
1180             /* The PTE that backs it should also be valdi */
1181             PointerPte = MiAddressToPte(PointerPte);
1182             ASSERT(PointerPte->u.Hard.Valid == 1);
1183 
1184             /* Get the original prototype PTE and turn it into a transition PTE */
1185             PointerPte = Pfn1->PteAddress;
1186             TempPte = *PointerPte;
1187             TempPte.u.Soft.Transition = 1;
1188             TempPte.u.Soft.Valid = 0;
1189             TempPte.u.Soft.Prototype = 0;
1190             TempPte.u.Soft.Protection = Pfn1->OriginalPte.u.Soft.Protection;
1191             MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1192             DPRINT("Marking PTE: %p as transition (%p - %lx)\n", PointerPte, Pfn1, MiGetPfnEntryIndex(Pfn1));
1193         }
1194 
1195         /* Put the page in transition */
1196         Pfn1->u3.e1.PageLocation = TransitionPage;
1197 
1198         /* PFN lock must be held */
1199         MI_ASSERT_PFN_LOCK_HELD();
1200 
1201         if (Pfn1->u3.e2.ReferenceCount == 1)
1202         {
1203             /* Is there still a PFN for this page? */
1204             if (MI_IS_PFN_DELETED(Pfn1))
1205             {
1206                 /* Clear the last reference */
1207                 Pfn1->u3.e2.ReferenceCount = 0;
1208                 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0);
1209 
1210                 /* Mark the page temporarily as valid, we're going to make it free soon */
1211                 Pfn1->u3.e1.PageLocation = ActiveAndValid;
1212 
1213                 /* Bring it back into the free list */
1214                 MiInsertPageInFreeList(PageFrameIndex);
1215             }
1216             else
1217             {
1218                 /* PFN not yet deleted, drop a ref count */
1219                 MiDecrementReferenceCount(Pfn1, PageFrameIndex);
1220             }
1221         }
1222         else
1223         {
1224             /* Otherwise, just drop the reference count */
1225             InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1226         }
1227     }
1228 }
1229 
1230 VOID
1231 NTAPI
1232 MmDereferencePage(PFN_NUMBER Pfn);
1233 
1234 VOID
1235 NTAPI
1236 MiDecrementReferenceCount(IN PMMPFN Pfn1,
1237                           IN PFN_NUMBER PageFrameIndex)
1238 {
1239     /* PFN lock must be held */
1240     MI_ASSERT_PFN_LOCK_HELD();
1241 
1242     /* Handle RosMm PFNs here, too (in case they got locked/unlocked by ARM3) */
1243     if (MI_IS_ROS_PFN(Pfn1))
1244     {
1245         MmDereferencePage(PageFrameIndex);
1246         return;
1247     }
1248 
1249     /* Sanity checks on the page */
1250     if (PageFrameIndex > MmHighestPhysicalPage ||
1251         Pfn1 != MI_PFN_ELEMENT(PageFrameIndex) ||
1252         Pfn1->u3.e2.ReferenceCount == 0 ||
1253         Pfn1->u3.e2.ReferenceCount >= 2500)
1254     {
1255         DPRINT1("PageFrameIndex=0x%lx, MmHighestPhysicalPage=0x%lx\n", PageFrameIndex, MmHighestPhysicalPage);
1256         DPRINT1("Pfn1=%p, Element=%p, RefCount=%u\n", Pfn1, MI_PFN_ELEMENT(PageFrameIndex), Pfn1->u3.e2.ReferenceCount);
1257         ASSERT(PageFrameIndex <= MmHighestPhysicalPage);
1258         ASSERT(Pfn1 == MI_PFN_ELEMENT(PageFrameIndex));
1259         ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
1260         ASSERT(Pfn1->u3.e2.ReferenceCount < 2500);
1261     }
1262 
1263     /* Dereference the page, bail out if it's still alive */
1264     InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
1265     if (Pfn1->u3.e2.ReferenceCount) return;
1266 
1267     /* Nobody should still have reference to this page */
1268     if (Pfn1->u2.ShareCount != 0)
1269     {
1270         /* Otherwise something's really wrong */
1271         KeBugCheckEx(PFN_LIST_CORRUPT, 7, PageFrameIndex, Pfn1->u2.ShareCount, 0);
1272     }
1273 
1274     /* And it should be lying on some page list */
1275     ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
1276 
1277     /* Did someone set the delete flag? */
1278     if (MI_IS_PFN_DELETED(Pfn1))
1279     {
1280         /* Insert it into the free list, there's nothing left to do */
1281         MiInsertPageInFreeList(PageFrameIndex);
1282         return;
1283     }
1284 
1285     /* Check to see which list this page should go into */
1286     if (Pfn1->u3.e1.Modified == 1)
1287     {
1288         /* Push it into the modified page list */
1289         MiInsertPageInList(&MmModifiedPageListHead, PageFrameIndex);
1290     }
1291     else
1292     {
1293         /* Otherwise, insert this page into the standby list */
1294         ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
1295         MiInsertStandbyListAtFront(PageFrameIndex);
1296     }
1297 }
1298 
1299 VOID
1300 NTAPI
1301 MiInitializePfnForOtherProcess(IN PFN_NUMBER PageFrameIndex,
1302                                IN PVOID PteAddress,
1303                                IN PFN_NUMBER PteFrame)
1304 {
1305     PMMPFN Pfn1;
1306 
1307     /* Setup the PTE */
1308     Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
1309     Pfn1->PteAddress = PteAddress;
1310 
1311     /* Make this a software PTE */
1312     MI_MAKE_SOFTWARE_PTE(&Pfn1->OriginalPte, MM_READWRITE);
1313 
1314     DPRINT("Called for %p from %p\n", Pfn1, _ReturnAddress());
1315 
1316     /* Setup the page */
1317     ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
1318     Pfn1->u3.e2.ReferenceCount = 1;
1319     Pfn1->u2.ShareCount = 1;
1320     Pfn1->u3.e1.PageLocation = ActiveAndValid;
1321     Pfn1->u3.e1.Modified = TRUE;
1322     Pfn1->u4.InPageError = FALSE;
1323 
1324     /* Did we get a PFN for the page table */
1325     if (PteFrame)
1326     {
1327         /* Store it */
1328         Pfn1->u4.PteFrame = PteFrame;
1329 
1330         /* Increase its share count so we don't get rid of it */
1331         Pfn1 = MI_PFN_ELEMENT(PteFrame);
1332 
1333         DPRINT("Incrementing %p from %p\n", Pfn1, _ReturnAddress());
1334         Pfn1->u2.ShareCount++;
1335     }
1336 }
1337 
1338 /* EOF */
1339