[NTOS]: Implement MiRemoveZeroPage and MiZeroPhysicalPage.
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / pfnlist.c
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 #line 15 "ARMĀ³::PFNLIST"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
18
19 #if DBG
20 #define ASSERT_LIST_INVARIANT(x) \
21 do { \
22 ASSERT(((x)->Total == 0 && \
23 (x)->Flink == LIST_HEAD && \
24 (x)->Blink == LIST_HEAD) || \
25 ((x)->Total != 0 && \
26 (x)->Flink != LIST_HEAD && \
27 (x)->Blink != LIST_HEAD)); \
28 } while (0)
29 #else
30 #define ASSERT_LIST_INVARIANT(x)
31 #endif
32
33 /* GLOBALS ********************************************************************/
34
35 BOOLEAN MmDynamicPfn;
36 BOOLEAN MmMirroring;
37
38 MMPFNLIST MmZeroedPageListHead = {0, ZeroedPageList, LIST_HEAD, LIST_HEAD};
39 MMPFNLIST MmFreePageListHead = {0, FreePageList, LIST_HEAD, LIST_HEAD};
40 MMPFNLIST MmStandbyPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
41 MMPFNLIST MmModifiedPageListHead = {0, ModifiedPageList, LIST_HEAD, LIST_HEAD};
42 MMPFNLIST MmModifiedNoWritePageListHead = {0, ModifiedNoWritePageList, LIST_HEAD, LIST_HEAD};
43 MMPFNLIST MmBadPageListHead = {0, BadPageList, LIST_HEAD, LIST_HEAD};
44 MMPFNLIST MmRomPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
45
46 PMMPFNLIST MmPageLocationList[] =
47 {
48 &MmZeroedPageListHead,
49 &MmFreePageListHead,
50 &MmStandbyPageListHead,
51 &MmModifiedPageListHead,
52 &MmModifiedNoWritePageListHead,
53 &MmBadPageListHead,
54 NULL,
55 NULL
56 };
57 /* FUNCTIONS ******************************************************************/
58
59 VOID
60 NTAPI
61 MiZeroPhysicalPage(IN PFN_NUMBER PageFrameIndex)
62 {
63 KIRQL OldIrql;
64 PVOID VirtualAddress;
65 PEPROCESS Process = PsGetCurrentProcess();
66
67 /* Map in hyperspace, then wipe it using XMMI or MEMSET */
68 VirtualAddress = MiMapPageInHyperSpace(Process, PageFrameIndex, &OldIrql);
69 KeZeroPages(VirtualAddress, PAGE_SIZE);
70 MiUnmapPageInHyperSpace(Process, VirtualAddress, OldIrql);
71 }
72
73 VOID
74 NTAPI
75 MiInsertInListTail(IN PMMPFNLIST ListHead,
76 IN PMMPFN Entry)
77 {
78 PFN_NUMBER OldBlink, EntryIndex = MiGetPfnEntryIndex(Entry);
79 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
80 ASSERT_LIST_INVARIANT(ListHead);
81
82 /* Get the back link */
83 OldBlink = ListHead->Blink;
84 if (OldBlink != LIST_HEAD)
85 {
86 /* Set the back pointer to point to us now */
87 MiGetPfnEntry(OldBlink)->u1.Flink = EntryIndex;
88 }
89 else
90 {
91 /* Set the list to point to us */
92 ListHead->Flink = EntryIndex;
93 }
94
95 /* Set the entry to point to the list head forwards, and the old page backwards */
96 Entry->u1.Flink = LIST_HEAD;
97 Entry->u2.Blink = OldBlink;
98
99 /* And now the head points back to us, since we are last */
100 ListHead->Blink = EntryIndex;
101 ListHead->Total++;
102 ASSERT_LIST_INVARIANT(ListHead);
103 }
104
105 VOID
106 NTAPI
107 MiInsertZeroListAtBack(IN PFN_NUMBER EntryIndex)
108 {
109 PFN_NUMBER OldBlink;
110 PMMPFNLIST ListHead;
111 PMMPFN Pfn1;
112 #if 0
113 PMMPFN Blink;
114 ULONG Color;
115 PMMCOLOR_TABLES ColorHead;
116 #endif
117
118 /* Make sure the PFN lock is held */
119 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
120
121 /* Get the descriptor */
122 Pfn1 = MiGetPfnEntry(EntryIndex);
123 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
124 ASSERT(Pfn1->u4.MustBeCached == 0);
125 ASSERT(Pfn1->u3.e1.Rom == 0);
126 ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
127 ASSERT(Pfn1->u4.InPageError == 0);
128
129 /* Use the zero list */
130 ListHead = &MmZeroedPageListHead;
131 ASSERT_LIST_INVARIANT(ListHead);
132 ListHead->Total++;
133
134 /* Get the back link */
135 OldBlink = ListHead->Blink;
136 if (OldBlink != LIST_HEAD)
137 {
138 /* Set the back pointer to point to us now */
139 MiGetPfnEntry(OldBlink)->u1.Flink = EntryIndex;
140 }
141 else
142 {
143 /* Set the list to point to us */
144 ListHead->Flink = EntryIndex;
145 }
146
147 /* Set the entry to point to the list head forwards, and the old page backwards */
148 Pfn1->u1.Flink = LIST_HEAD;
149 Pfn1->u2.Blink = OldBlink;
150
151 /* And now the head points back to us, since we are last */
152 ListHead->Blink = EntryIndex;
153 ASSERT_LIST_INVARIANT(ListHead);
154
155 /* Update the page location */
156 Pfn1->u3.e1.PageLocation = ZeroedPageList;
157
158 /* FIXME: NOT YET Due to caller semantics: Update the available page count */
159 //MmAvailablePages++;
160
161 /* Check if we've reached the configured low memory threshold */
162 if (MmAvailablePages == MmLowMemoryThreshold)
163 {
164 /* Clear the event, because now we're ABOVE the threshold */
165 KeClearEvent(MiLowMemoryEvent);
166 }
167 else if (MmAvailablePages == MmHighMemoryThreshold)
168 {
169 /* Otherwise check if we reached the high threshold and signal the event */
170 KeSetEvent(MiHighMemoryEvent, 0, FALSE);
171 }
172
173 #if 0
174 /* Get the page color */
175 Color = EntryIndex & MmSecondaryColorMask;
176
177 /* Get the first page on the color list */
178 ColorHead = &MmFreePagesByColor[ZeroedPageList][Color];
179 if (ColorHead->Flink == LIST_HEAD)
180 {
181 /* The list is empty, so we are the first page */
182 Pfn1->u4.PteFrame = -1;
183 ColorHead->Flink = EntryIndex;
184 }
185 else
186 {
187 /* Get the previous page */
188 Blink = (PMMPFN)ColorHead->Blink;
189
190 /* Make it link to us */
191 Pfn1->u4.PteFrame = MiGetPfnEntryIndex(Blink);
192 Blink->OriginalPte.u.Long = EntryIndex;
193 }
194
195 /* Now initialize our own list pointers */
196 ColorHead->Blink = Pfn1;
197 Pfn1->OriginalPte.u.Long = LIST_HEAD;
198
199 /* And increase the count in the colored list */
200 ColorHead->Count++;
201 #endif
202 }
203
204 VOID
205 NTAPI
206 MiUnlinkFreeOrZeroedPage(IN PMMPFN Entry)
207 {
208 PFN_NUMBER OldFlink, OldBlink;
209 PMMPFNLIST ListHead;
210 MMLISTS ListName;
211
212 /* Make sure the PFN lock is held */
213 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
214
215 /* Make sure the PFN entry isn't in-use */
216 ASSERT(Entry->u3.e1.WriteInProgress == 0);
217 ASSERT(Entry->u3.e1.ReadInProgress == 0);
218
219 /* Find the list for this entry, make sure it's the free or zero list */
220 ListHead = MmPageLocationList[Entry->u3.e1.PageLocation];
221 ListName = ListHead->ListName;
222 ASSERT(ListHead != NULL);
223 ASSERT(ListName <= FreePageList);
224 ASSERT_LIST_INVARIANT(ListHead);
225
226 /* Remove one count */
227 ASSERT(ListHead->Total != 0);
228 ListHead->Total--;
229
230 /* Get the forward and back pointers */
231 OldFlink = Entry->u1.Flink;
232 OldBlink = Entry->u2.Blink;
233
234 /* Check if the next entry is the list head */
235 if (OldFlink != LIST_HEAD)
236 {
237 /* It is not, so set the backlink of the actual entry, to our backlink */
238 MiGetPfnEntry(OldFlink)->u2.Blink = OldBlink;
239 }
240 else
241 {
242 /* Set the list head's backlink instead */
243 ListHead->Blink = OldBlink;
244 }
245
246 /* Check if the back entry is the list head */
247 if (OldBlink != LIST_HEAD)
248 {
249 /* It is not, so set the backlink of the actual entry, to our backlink */
250 MiGetPfnEntry(OldBlink)->u1.Flink = OldFlink;
251 }
252 else
253 {
254 /* Set the list head's backlink instead */
255 ListHead->Flink = OldFlink;
256 }
257
258 /* We are not on a list anymore */
259 Entry->u1.Flink = Entry->u2.Blink = 0;
260 ASSERT_LIST_INVARIANT(ListHead);
261
262 /* FIXME: Deal with color list */
263
264 /* See if we hit any thresholds */
265 if (MmAvailablePages == MmHighMemoryThreshold)
266 {
267 /* Clear the high memory event */
268 KeClearEvent(MiHighMemoryEvent);
269 }
270 else if (MmAvailablePages == MmLowMemoryThreshold)
271 {
272 /* Signal the low memory event */
273 KeSetEvent(MiLowMemoryEvent, 0, FALSE);
274 }
275
276 /* One less page */
277 if (--MmAvailablePages < MmMinimumFreePages)
278 {
279 /* FIXME: Should wake up the MPW and working set manager, if we had one */
280 }
281 }
282
283 PFN_NUMBER
284 NTAPI
285 MiRemovePageByColor(IN PFN_NUMBER PageIndex,
286 IN ULONG Color)
287 {
288 PMMPFN Pfn1;
289 PMMPFNLIST ListHead;
290 MMLISTS ListName;
291 PFN_NUMBER OldFlink, OldBlink;
292 ULONG OldColor, OldCache;
293 #if 0
294 PMMCOLOR_TABLES ColorTable;
295 #endif
296 /* Make sure PFN lock is held */
297 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
298 ASSERT(Color < MmSecondaryColors);
299
300 /* Get the PFN entry */
301 Pfn1 = MiGetPfnEntry(PageIndex);
302 ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
303 ASSERT(Pfn1->u3.e1.Rom == 0);
304
305 /* Capture data for later */
306 OldColor = Pfn1->u3.e1.PageColor;
307 OldCache = Pfn1->u3.e1.CacheAttribute;
308
309 /* Could be either on free or zero list */
310 ListHead = MmPageLocationList[Pfn1->u3.e1.PageLocation];
311 ASSERT_LIST_INVARIANT(ListHead);
312 ListName = ListHead->ListName;
313 ASSERT(ListName <= FreePageList);
314
315 /* Remove a page */
316 ListHead->Total--;
317
318 /* Get the forward and back pointers */
319 OldFlink = Pfn1->u1.Flink;
320 OldBlink = Pfn1->u2.Blink;
321
322 /* Check if the next entry is the list head */
323 if (OldFlink != LIST_HEAD)
324 {
325 /* It is not, so set the backlink of the actual entry, to our backlink */
326 MiGetPfnEntry(OldFlink)->u2.Blink = OldBlink;
327 }
328 else
329 {
330 /* Set the list head's backlink instead */
331 ListHead->Blink = OldFlink;
332 }
333
334 /* Check if the back entry is the list head */
335 if (OldBlink != LIST_HEAD)
336 {
337 /* It is not, so set the backlink of the actual entry, to our backlink */
338 MiGetPfnEntry(OldBlink)->u1.Flink = OldFlink;
339 }
340 else
341 {
342 /* Set the list head's backlink instead */
343 ListHead->Flink = OldFlink;
344 }
345
346 /* We are not on a list anymore */
347 ASSERT_LIST_INVARIANT(ListHead);
348 Pfn1->u1.Flink = Pfn1->u2.Blink = 0;
349
350 /* Zero flags but restore color and cache */
351 Pfn1->u3.e2.ShortFlags = 0;
352 Pfn1->u3.e1.PageColor = OldColor;
353 Pfn1->u3.e1.CacheAttribute = OldCache;
354
355 #if 0 // When switching to ARM3
356 /* Get the first page on the color list */
357 ColorTable = &MmFreePagesByColor[ListName][Color];
358 ASSERT(ColorTable->Count >= 1);
359
360 /* Set the forward link to whoever we were pointing to */
361 ColorTable->Flink = Pfn1->OriginalPte.u.Long;
362 if (ColorTable->Flink == LIST_HEAD)
363 {
364 /* This is the beginning of the list, so set the sentinel value */
365 ColorTable->Blink = LIST_HEAD;
366 }
367 else
368 {
369 /* The list is empty, so we are the first page */
370 MiGetPfnEntry(ColorTable->Flink)->u4.PteFrame = -1;
371 }
372
373 /* One more page */
374 ColorTable->Total++;
375 #endif
376 /* See if we hit any thresholds */
377 if (MmAvailablePages == MmHighMemoryThreshold)
378 {
379 /* Clear the high memory event */
380 KeClearEvent(MiHighMemoryEvent);
381 }
382 else if (MmAvailablePages == MmLowMemoryThreshold)
383 {
384 /* Signal the low memory event */
385 KeSetEvent(MiLowMemoryEvent, 0, FALSE);
386 }
387
388 /* One less page */
389 if (--MmAvailablePages < MmMinimumFreePages)
390 {
391 /* FIXME: Should wake up the MPW and working set manager, if we had one */
392 }
393
394 /* Return the page */
395 return PageIndex;
396 }
397
398 PFN_NUMBER
399 NTAPI
400 MiRemoveAnyPage(IN ULONG Color)
401 {
402 PFN_NUMBER PageIndex;
403 PMMPFN Pfn1;
404
405 /* Make sure PFN lock is held and we have pages */
406 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
407 ASSERT(MmAvailablePages != 0);
408 ASSERT(Color < MmSecondaryColors);
409
410 /* Check the colored free list */
411 #if 0 // Enable when using ARM3 database */
412 PageIndex = MmFreePagesByColor[FreePageList][Color].Flink;
413 if (PageIndex == LIST_HEAD)
414 {
415 /* Check the colored zero list */
416 PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
417 if (PageIndex == LIST_HEAD)
418 {
419 #endif
420 /* Check the free list */
421 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
422 PageIndex = MmFreePageListHead.Flink;
423 Color = PageIndex & MmSecondaryColorMask;
424 if (PageIndex == LIST_HEAD)
425 {
426 /* Check the zero list */
427 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
428 PageIndex = MmZeroedPageListHead.Flink;
429 Color = PageIndex & MmSecondaryColorMask;
430 ASSERT(PageIndex != LIST_HEAD);
431 if (PageIndex == LIST_HEAD)
432 {
433 /* FIXME: Should check the standby list */
434 ASSERT(MmZeroedPageListHead.Total == 0);
435 }
436 }
437 #if 0 // Enable when using ARM3 database */
438 }
439 }
440 #endif
441
442 /* Remove the page from its list */
443 PageIndex = MiRemovePageByColor(PageIndex, Color);
444
445 /* Sanity checks */
446 Pfn1 = MiGetPfnEntry(PageIndex);
447 ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) ||
448 (Pfn1->u3.e1.PageLocation == ZeroedPageList));
449 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
450 ASSERT(Pfn1->u2.ShareCount == 0);
451 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
452 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
453
454 /* Return the page */
455 return PageIndex;
456 }
457
458 PFN_NUMBER
459 NTAPI
460 MiRemoveZeroPage(IN ULONG Color)
461 {
462 PFN_NUMBER PageIndex;
463 PMMPFN Pfn1;
464 BOOLEAN Zero;
465
466 /* Make sure PFN lock is held and we have pages */
467 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
468 ASSERT(MmAvailablePages != 0);
469 ASSERT(Color < MmSecondaryColors);
470
471 /* Check the colored zero list */
472 #if 0 // Enable when using ARM3 database */
473 PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
474 if (PageIndex == LIST_HEAD)
475 {
476 #endif
477 /* Check the zero list */
478 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
479 PageIndex = MmZeroedPageListHead.Flink;
480 Color = PageIndex & MmSecondaryColorMask;
481 if (PageIndex == LIST_HEAD)
482 {
483 ASSERT(MmZeroedPageListHead.Total == 0);
484 Zero = TRUE;
485 #if 0 // Enable when using ARM3 database */
486 /* Check the colored free list */
487 PageIndex = MmFreePagesByColor[ZeroedPageList][Color].Flink;
488 if (PageIndex == LIST_HEAD)
489 {
490 #endif
491 /* Check the free list */
492 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
493 PageIndex = MmFreePageListHead.Flink;
494 Color = PageIndex & MmSecondaryColorMask;
495 ASSERT(PageIndex != LIST_HEAD);
496 if (PageIndex == LIST_HEAD)
497 {
498 /* FIXME: Should check the standby list */
499 ASSERT(MmZeroedPageListHead.Total == 0);
500 }
501 #if 0 // Enable when using ARM3 database */
502 }
503 #endif
504 }
505 #if 0 // Enable when using ARM3 database */
506 }
507 #endif
508 /* Sanity checks */
509 Pfn1 = MiGetPfnEntry(PageIndex);
510 ASSERT((Pfn1->u3.e1.PageLocation == FreePageList) ||
511 (Pfn1->u3.e1.PageLocation == ZeroedPageList));
512
513 /* Remove the page from its list */
514 PageIndex = MiRemovePageByColor(PageIndex, Color);
515 ASSERT(Pfn1 == MiGetPfnEntry(PageIndex));
516
517 /* Zero it, if needed */
518 if (Zero) MiZeroPhysicalPage(PageIndex);
519
520 /* Sanity checks */
521 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
522 ASSERT(Pfn1->u2.ShareCount == 0);
523 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
524 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
525
526 /* Return the page */
527 return PageIndex;
528 }
529 \f
530
531 PMMPFN
532 NTAPI
533 MiRemoveHeadList(IN PMMPFNLIST ListHead)
534 {
535 PFN_NUMBER Entry, Flink;
536 PMMPFN Pfn1;
537 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
538 ASSERT_LIST_INVARIANT(ListHead);
539
540 /* Get the entry that's currently first on the list */
541 Entry = ListHead->Flink;
542 Pfn1 = MiGetPfnEntry(Entry);
543
544 /* Make the list point to the entry following the first one */
545 Flink = Pfn1->u1.Flink;
546 ListHead->Flink = Flink;
547
548 /* Check if the next entry is actually the list head */
549 if (ListHead->Flink != LIST_HEAD)
550 {
551 /* It isn't, so therefore whoever is coming next points back to the head */
552 MiGetPfnEntry(Flink)->u2.Blink = LIST_HEAD;
553 }
554 else
555 {
556 /* Then the list is empty, so the backlink should point back to us */
557 ListHead->Blink = LIST_HEAD;
558 }
559
560 /* We are not on a list anymore */
561 Pfn1->u1.Flink = Pfn1->u2.Blink = 0;
562 ListHead->Total--;
563 ASSERT_LIST_INVARIANT(ListHead);
564
565 /* Return the head element */
566 return Pfn1;
567 }
568
569 VOID
570 NTAPI
571 MiInsertPageInFreeList(IN PFN_NUMBER PageFrameIndex)
572 {
573 PMMPFNLIST ListHead;
574 PFN_NUMBER LastPage;
575 PMMPFN Pfn1;
576 #if 0
577 ULONG Color;
578 PMMPFN Blink;
579 PMMCOLOR_TABLES ColorTable;
580 #endif
581 /* Make sure the page index is valid */
582 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
583 ASSERT((PageFrameIndex != 0) &&
584 (PageFrameIndex <= MmHighestPhysicalPage) &&
585 (PageFrameIndex >= MmLowestPhysicalPage));
586
587 /* Get the PFN entry */
588 Pfn1 = MiGetPfnEntry(PageFrameIndex);
589
590 /* Sanity checks that a right kind of page is being inserted here */
591 ASSERT(Pfn1->u4.MustBeCached == 0);
592 ASSERT(Pfn1->u3.e1.Rom != 1);
593 ASSERT(Pfn1->u3.e1.RemovalRequested == 0);
594 ASSERT(Pfn1->u4.VerifierAllocation == 0);
595 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
596
597 /* Get the free page list and increment its count */
598 ListHead = &MmFreePageListHead;
599 ASSERT_LIST_INVARIANT(ListHead);
600 ListHead->Total++;
601
602 /* Get the last page on the list */
603 LastPage = ListHead->Blink;
604 if (LastPage != LIST_HEAD)
605 {
606 /* Link us with the previous page, so we're at the end now */
607 MiGetPfnEntry(LastPage)->u1.Flink = PageFrameIndex;
608 }
609 else
610 {
611 /* The list is empty, so we are the first page */
612 ListHead->Flink = PageFrameIndex;
613 }
614
615 /* Now make the list head point back to us (since we go at the end) */
616 ListHead->Blink = PageFrameIndex;
617 ASSERT_LIST_INVARIANT(ListHead);
618
619 /* And initialize our own list pointers */
620 Pfn1->u1.Flink = LIST_HEAD;
621 Pfn1->u2.Blink = LastPage;
622
623 /* Set the list name and default priority */
624 Pfn1->u3.e1.PageLocation = FreePageList;
625 Pfn1->u4.Priority = 3;
626
627 /* Clear some status fields */
628 Pfn1->u4.InPageError = 0;
629 Pfn1->u4.AweAllocation = 0;
630
631 /* Increase available pages */
632 MmAvailablePages++;
633
634 /* Check if we've reached the configured low memory threshold */
635 if (MmAvailablePages == MmLowMemoryThreshold)
636 {
637 /* Clear the event, because now we're ABOVE the threshold */
638 KeClearEvent(MiLowMemoryEvent);
639 }
640 else if (MmAvailablePages == MmHighMemoryThreshold)
641 {
642 /* Otherwise check if we reached the high threshold and signal the event */
643 KeSetEvent(MiHighMemoryEvent, 0, FALSE);
644 }
645
646 #if 0 // When using ARM3 PFN
647 /* Get the page color */
648 Color = PageFrameIndex & MmSecondaryColorMask;
649
650 /* Get the first page on the color list */
651 ColorTable = &MmFreePagesByColor[FreePageList][Color];
652 if (ColorTable->Flink == LIST_HEAD)
653 {
654 /* The list is empty, so we are the first page */
655 Pfn1->u4.PteFrame = -1;
656 ColorTable->Flink = PageFrameIndex;
657 }
658 else
659 {
660 /* Get the previous page */
661 Blink = (PMMPFN)ColorTable->Blink;
662
663 /* Make it link to us */
664 Pfn1->u4.PteFrame = MI_PFNENTRY_TO_PFN(Blink);
665 Blink->OriginalPte.u.Long = PageFrameIndex;
666 }
667
668 /* Now initialize our own list pointers */
669 ColorTable->Blink = Pfn1;
670 Pfn1->OriginalPte.u.Long = LIST_HEAD;
671
672 /* And increase the count in the colored list */
673 ColorTable->Count++;
674 #endif
675
676 /* Notify zero page thread if enough pages are on the free list now */
677 extern KEVENT ZeroPageThreadEvent;
678 if ((MmFreePageListHead.Total > 8) && !(KeReadStateEvent(&ZeroPageThreadEvent)))
679 {
680 /* This is ReactOS-specific */
681 KeSetEvent(&ZeroPageThreadEvent, IO_NO_INCREMENT, FALSE);
682 }
683 }
684
685 VOID
686 NTAPI
687 MiInitializePfn(IN PFN_NUMBER PageFrameIndex,
688 IN PMMPTE PointerPte,
689 IN BOOLEAN Modified)
690 {
691 PMMPFN Pfn1;
692 NTSTATUS Status;
693 PMMPTE PointerPtePte;
694 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
695
696 /* Setup the PTE */
697 Pfn1 = MiGetPfnEntry(PageFrameIndex);
698 Pfn1->PteAddress = PointerPte;
699
700 /* Check if this PFN is part of a valid address space */
701 if (PointerPte->u.Hard.Valid == 1)
702 {
703 /* FIXME: TODO */
704 ASSERT(FALSE);
705 }
706
707 /* Otherwise this is a fresh page -- set it up */
708 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
709 Pfn1->u3.e2.ReferenceCount = 1;
710 Pfn1->u2.ShareCount = 1;
711 Pfn1->u3.e1.PageLocation = ActiveAndValid;
712 ASSERT(Pfn1->u3.e1.Rom == 0);
713 Pfn1->u3.e1.Modified = Modified;
714
715 /* Get the page table for the PTE */
716 PointerPtePte = MiAddressToPte(PointerPte);
717 if (PointerPtePte->u.Hard.Valid == 0)
718 {
719 /* Make sure the PDE gets paged in properly */
720 Status = MiCheckPdeForPagedPool(PointerPte);
721 if (!NT_SUCCESS(Status))
722 {
723 /* Crash */
724 KeBugCheckEx(MEMORY_MANAGEMENT,
725 0x61940,
726 (ULONG_PTR)PointerPte,
727 (ULONG_PTR)PointerPtePte->u.Long,
728 (ULONG_PTR)MiPteToAddress(PointerPte));
729 }
730 }
731
732 /* Get the PFN for the page table */
733 PageFrameIndex = PFN_FROM_PTE(PointerPtePte);
734 ASSERT(PageFrameIndex != 0);
735 Pfn1->u4.PteFrame = PageFrameIndex;
736
737 /* Increase its share count so we don't get rid of it */
738 Pfn1 = MiGetPfnEntry(PageFrameIndex);
739 Pfn1->u2.ShareCount++;
740 }
741
742 PFN_NUMBER
743 NTAPI
744 MiAllocatePfn(IN PMMPTE PointerPte,
745 IN ULONG Protection)
746 {
747 KIRQL OldIrql;
748 PFN_NUMBER PageFrameIndex;
749 MMPTE TempPte;
750
751 /* Make an empty software PTE */
752 MI_MAKE_SOFTWARE_PTE(&TempPte, MM_READWRITE);
753
754 /* Lock the PFN database */
755 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
756
757 /* Check if we're running low on pages */
758 if (MmAvailablePages < 128)
759 {
760 DPRINT1("Warning, running low on memory: %d pages left\n", MmAvailablePages);
761 //MiEnsureAvailablePageOrWait(NULL, OldIrql);
762 }
763
764 /* Grab a page */
765 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
766 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
767 PageFrameIndex = MiRemoveAnyPage(0);
768
769 /* Write the software PTE */
770 ASSERT(PointerPte->u.Hard.Valid == 0);
771 *PointerPte = TempPte;
772 PointerPte->u.Soft.Protection |= Protection;
773
774 /* Initialize its PFN entry */
775 MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
776
777 /* Release the PFN lock and return the page */
778 ASSERT_LIST_INVARIANT(&MmFreePageListHead);
779 ASSERT_LIST_INVARIANT(&MmZeroedPageListHead);
780 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
781 return PageFrameIndex;
782 }
783
784 VOID
785 NTAPI
786 MiDecrementShareCount(IN PMMPFN Pfn1,
787 IN PFN_NUMBER PageFrameIndex)
788 {
789 ASSERT(PageFrameIndex > 0);
790 ASSERT(MiGetPfnEntry(PageFrameIndex) != NULL);
791 ASSERT(Pfn1 == MiGetPfnEntry(PageFrameIndex));
792
793 /* Page must be in-use */
794 if ((Pfn1->u3.e1.PageLocation != ActiveAndValid) &&
795 (Pfn1->u3.e1.PageLocation != StandbyPageList))
796 {
797 /* Otherwise we have PFN corruption */
798 KeBugCheckEx(PFN_LIST_CORRUPT,
799 0x99,
800 PageFrameIndex,
801 Pfn1->u3.e1.PageLocation,
802 0);
803 }
804
805 /* Check if the share count is now 0 */
806 ASSERT(Pfn1->u2.ShareCount < 0xF000000);
807 if (!--Pfn1->u2.ShareCount)
808 {
809 /* ReactOS does not handle these */
810 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
811
812 /* Put the page in transition */
813 Pfn1->u3.e1.PageLocation = TransitionPage;
814
815 /* PFN lock must be held */
816 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
817
818 /* Page should at least have one reference */
819 ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
820 if (Pfn1->u3.e2.ReferenceCount == 1)
821 {
822 /* In ReactOS, this path should always be hit with a deleted PFN */
823 ASSERT(MI_IS_PFN_DELETED(Pfn1) == TRUE);
824
825 /* Clear the last reference */
826 Pfn1->u3.e2.ReferenceCount = 0;
827
828 /*
829 * OriginalPte is used by AweReferenceCount in ReactOS, but either
830 * ways we shouldn't be seeing RMAP entries at this point
831 */
832 ASSERT(Pfn1->OriginalPte.u.Soft.Prototype == 0);
833 ASSERT(Pfn1->OriginalPte.u.Long == 0);
834
835 /* Mark the page temporarily as valid, we're going to make it free soon */
836 Pfn1->u3.e1.PageLocation = ActiveAndValid;
837
838 /* Bring it back into the free list */
839 MiInsertPageInFreeList(PageFrameIndex);
840 }
841 else
842 {
843 /* Otherwise, just drop the reference count */
844 InterlockedDecrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
845 }
846 }
847 }
848
849 VOID
850 NTAPI
851 MiInitializePfnForOtherProcess(IN PFN_NUMBER PageFrameIndex,
852 IN PMMPTE PointerPte,
853 IN PFN_NUMBER PteFrame)
854 {
855 PMMPFN Pfn1;
856
857 /* Setup the PTE */
858 Pfn1 = MiGetPfnEntry(PageFrameIndex);
859 Pfn1->PteAddress = PointerPte;
860
861 #if 0 // When using ARM3 PFN
862 /* Make this a software PTE */
863 MI_MAKE_SOFTWARE_PTE(&Pfn1->OriginalPte, MM_READWRITE);
864 #endif
865
866 /* Setup the page */
867 ASSERT(Pfn1->u3.e2.ReferenceCount == 0);
868 Pfn1->u3.e2.ReferenceCount = 1;
869 Pfn1->u2.ShareCount = 1;
870 Pfn1->u3.e1.PageLocation = ActiveAndValid;
871 Pfn1->u3.e1.Modified = TRUE;
872 Pfn1->u4.InPageError = FALSE;
873
874 /* Did we get a PFN for the page table */
875 if (PteFrame)
876 {
877 /* Store it */
878 Pfn1->u4.PteFrame = PteFrame;
879
880 /* Increase its share count so we don't get rid of it */
881 Pfn1 = MiGetPfnEntry(PteFrame);
882 Pfn1->u2.ShareCount++;
883 }
884 }
885
886 /* EOF */