2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/mm/freelist.c
5 * PURPOSE: Handle the list of free physical pages
6 * PROGRAMMER: David Welch (welch@cwcom.net)
9 * 18/08/98: Added a fix from Robert Bergkvist
12 /* INCLUDES ****************************************************************/
14 #include <ddk/ntddk.h>
15 #include <internal/mm.h>
16 #include <internal/mmhal.h>
17 #include <internal/ntoskrnl.h>
20 #include <internal/debug.h>
22 /* TYPES *******************************************************************/
24 #define MM_PHYSICAL_PAGE_FREE (0x1)
25 #define MM_PHYSICAL_PAGE_USED (0x2)
26 #define MM_PHYSICAL_PAGE_BIOS (0x3)
28 #define MM_PTYPE(x) ((x) & 0x3)
30 typedef struct _PHYSICAL_PAGE
36 SWAPENTRY SavedSwapEntry
;
38 } PHYSICAL_PAGE
, *PPHYSICAL_PAGE
;
40 /* GLOBALS ****************************************************************/
42 static PPHYSICAL_PAGE MmPageArray
;
44 static LIST_ENTRY UsedPageListHead
;
45 static KSPIN_LOCK PageListLock
;
46 static LIST_ENTRY FreePageListHead
;
47 static LIST_ENTRY BiosPageListHead
;
49 /* FUNCTIONS *************************************************************/
52 MmGetContinuousPages(ULONG NumberOfBytes
,
53 PHYSICAL_ADDRESS HighestAcceptableAddress
)
61 NrPages
= PAGE_ROUND_UP(NumberOfBytes
) / PAGESIZE
;
63 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
67 for (i
= 0; i
< (HighestAcceptableAddress
.QuadPart
/ PAGESIZE
); i
++)
69 if (MmPageArray
[i
].Flags
& MM_PHYSICAL_PAGE_FREE
)
80 if (length
== NrPages
)
92 KeReleaseSpinLock(&PageListLock
, oldIrql
);
95 for (i
= start
; i
< (start
+ length
); i
++)
97 RemoveEntryList(&MmPageArray
[i
].ListEntry
);
98 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_USED
;
99 MmPageArray
[i
].ReferenceCount
= 1;
100 MmPageArray
[i
].LockCount
= 0;
101 MmPageArray
[i
].SavedSwapEntry
= 0;
102 InsertTailList(&UsedPageListHead
, &MmPageArray
[i
].ListEntry
);
104 return((PVOID
)(start
* 4096));
107 PVOID
MmInitializePageList(PVOID FirstPhysKernelAddress
,
108 PVOID LastPhysKernelAddress
,
109 ULONG MemorySizeInPages
,
110 ULONG LastKernelAddress
)
112 * FUNCTION: Initializes the page list with all pages free
113 * except those known to be reserved and those used by the kernel
115 * PageBuffer = Page sized buffer
116 * FirstKernelAddress = First physical address used by the kernel
117 * LastKernelAddress = Last physical address used by the kernel
124 DPRINT("MmInitializePageList(FirstPhysKernelAddress %x, "
125 "LastPhysKernelAddress %x, "
126 "MemorySizeInPages %x, LastKernelAddress %x)\n",
127 FirstPhysKernelAddress
,
128 LastPhysKernelAddress
,
132 InitializeListHead(&UsedPageListHead
);
133 KeInitializeSpinLock(&PageListLock
);
134 InitializeListHead(&FreePageListHead
);
135 InitializeListHead(&BiosPageListHead
);
137 Reserved
= (MemorySizeInPages
* sizeof(PHYSICAL_PAGE
)) / PAGESIZE
;
138 MmPageArray
= (PHYSICAL_PAGE
*)LastKernelAddress
;
140 DPRINT("Reserved %d\n", Reserved
);
142 LastKernelAddress
= PAGE_ROUND_UP(LastKernelAddress
);
143 LastKernelAddress
= ((ULONG
)LastKernelAddress
+ (Reserved
* PAGESIZE
));
144 LastPhysKernelAddress
= (PVOID
)PAGE_ROUND_UP(LastPhysKernelAddress
);
145 LastPhysKernelAddress
= LastPhysKernelAddress
+ (Reserved
* PAGESIZE
);
147 MmStats
.NrTotalPages
= 0;
148 MmStats
.NrSystemPages
= 0;
149 MmStats
.NrUserPages
= 0;
150 MmStats
.NrReservedPages
= 0;
151 MmStats
.NrFreePages
= 0;
152 MmStats
.NrLockedPages
= 0;
154 for (i
= 0; i
< Reserved
; i
++)
156 if (!MmIsPagePresent(NULL
,
157 (PVOID
)((ULONG
)MmPageArray
+ (i
* PAGESIZE
))))
159 Status
= MmCreateVirtualMapping(NULL
,
160 (PVOID
)((ULONG
)MmPageArray
+
163 (ULONG
)(LastPhysKernelAddress
165 if (!NT_SUCCESS(Status
))
167 DbgPrint("Unable to create virtual mapping\n");
174 if ((ULONG
)FirstPhysKernelAddress
< 0xa0000)
176 MmStats
.NrFreePages
+= ((ULONG
)FirstPhysKernelAddress
/PAGESIZE
);
177 for (; i
<((ULONG
)FirstPhysKernelAddress
/PAGESIZE
); i
++)
179 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_FREE
;
180 MmPageArray
[i
].ReferenceCount
= 0;
181 KeInitializeEvent(&MmPageArray
[i
].Event
,
184 InsertTailList(&FreePageListHead
,
185 &MmPageArray
[i
].ListEntry
);
187 MmStats
.NrSystemPages
+=
188 ((((ULONG
)LastPhysKernelAddress
) / PAGESIZE
) - i
);
189 for (; i
<((ULONG
)LastPhysKernelAddress
/ PAGESIZE
); i
++)
191 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_USED
;
192 MmPageArray
[i
].ReferenceCount
= 1;
193 KeInitializeEvent(&MmPageArray
[i
].Event
,
196 InsertTailList(&UsedPageListHead
,
197 &MmPageArray
[i
].ListEntry
);
199 MmStats
.NrFreePages
+= ((0xa0000/PAGESIZE
) - i
);
200 for (; i
<(0xa0000/PAGESIZE
); i
++)
202 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_FREE
;
203 MmPageArray
[i
].ReferenceCount
= 0;
204 KeInitializeEvent(&MmPageArray
[i
].Event
,
207 InsertTailList(&FreePageListHead
,
208 &MmPageArray
[i
].ListEntry
);
210 MmStats
.NrReservedPages
+= ((0x100000/PAGESIZE
) - i
);
211 for (; i
<(0x100000 / PAGESIZE
); i
++)
213 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_BIOS
;
214 MmPageArray
[i
].ReferenceCount
= 1;
215 KeInitializeEvent(&MmPageArray
[i
].Event
,
218 InsertTailList(&BiosPageListHead
,
219 &MmPageArray
[i
].ListEntry
);
224 MmStats
.NrFreePages
+= (0xa0000 / PAGESIZE
);
225 for (; i
<(0xa0000 / PAGESIZE
); i
++)
227 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_FREE
;
228 MmPageArray
[i
].ReferenceCount
= 0;
229 KeInitializeEvent(&MmPageArray
[i
].Event
,
232 InsertTailList(&FreePageListHead
,
233 &MmPageArray
[i
].ListEntry
);
235 MmStats
.NrReservedPages
+= (0x60000 / PAGESIZE
);
236 for (; i
<(0x100000 / PAGESIZE
); i
++)
238 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_BIOS
;
239 MmPageArray
[i
].ReferenceCount
= 1;
240 KeInitializeEvent(&MmPageArray
[i
].Event
,
243 InsertTailList(&BiosPageListHead
,
244 &MmPageArray
[i
].ListEntry
);
246 MmStats
.NrFreePages
+= (((ULONG
)FirstPhysKernelAddress
/PAGESIZE
) - i
);
247 for (; i
<((ULONG
)FirstPhysKernelAddress
/PAGESIZE
); i
++)
249 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_FREE
;
250 MmPageArray
[i
].ReferenceCount
= 0;
251 KeInitializeEvent(&MmPageArray
[i
].Event
,
254 InsertTailList(&FreePageListHead
,
255 &MmPageArray
[i
].ListEntry
);
257 MmStats
.NrSystemPages
+=
258 (((ULONG
)LastPhysKernelAddress
/PAGESIZE
) - i
);
259 for (; i
<((ULONG
)LastPhysKernelAddress
/PAGESIZE
); i
++)
261 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_USED
;
262 MmPageArray
[i
].ReferenceCount
= 1;
263 KeInitializeEvent(&MmPageArray
[i
].Event
,
266 InsertTailList(&UsedPageListHead
,
267 &MmPageArray
[i
].ListEntry
);
271 MmStats
.NrFreePages
+= (MemorySizeInPages
- i
);
272 for (; i
<MemorySizeInPages
; i
++)
274 MmPageArray
[i
].Flags
= MM_PHYSICAL_PAGE_FREE
;
275 MmPageArray
[i
].ReferenceCount
= 0;
276 KeInitializeEvent(&MmPageArray
[i
].Event
,
279 InsertTailList(&FreePageListHead
,
280 &MmPageArray
[i
].ListEntry
);
282 return((PVOID
)LastKernelAddress
);
285 VOID
MmSetFlagsPage(PVOID PhysicalAddress
,
288 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
291 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
292 MmPageArray
[Start
].Flags
= Flags
;
293 KeReleaseSpinLock(&PageListLock
, oldIrql
);
296 ULONG
MmGetFlagsPage(PVOID PhysicalAddress
)
298 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
302 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
303 Flags
= MmPageArray
[Start
].Flags
;
304 KeReleaseSpinLock(&PageListLock
, oldIrql
);
310 VOID
MmSetSavedSwapEntryPage(PVOID PhysicalAddress
,
311 SWAPENTRY SavedSwapEntry
)
313 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
316 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
317 MmPageArray
[Start
].SavedSwapEntry
= SavedSwapEntry
;
318 KeReleaseSpinLock(&PageListLock
, oldIrql
);
321 SWAPENTRY
MmGetSavedSwapEntryPage(PVOID PhysicalAddress
)
323 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
324 SWAPENTRY SavedSwapEntry
;
327 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
328 SavedSwapEntry
= MmPageArray
[Start
].SavedSwapEntry
;
329 KeReleaseSpinLock(&PageListLock
, oldIrql
);
331 return(SavedSwapEntry
);
334 VOID
MmReferencePage(PVOID PhysicalAddress
)
336 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
339 DPRINT("MmReferencePage(PhysicalAddress %x)\n", PhysicalAddress
);
341 if (((ULONG
)PhysicalAddress
) == 0)
346 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
348 if (MM_PTYPE(MmPageArray
[Start
].Flags
) != MM_PHYSICAL_PAGE_USED
)
350 DbgPrint("Referencing non-used page\n");
354 MmPageArray
[Start
].ReferenceCount
++;
355 KeReleaseSpinLock(&PageListLock
, oldIrql
);
358 VOID
MmDereferencePage(PVOID PhysicalAddress
)
360 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
363 DPRINT("MmDereferencePage(PhysicalAddress %x)\n", PhysicalAddress
);
365 if (((ULONG
)PhysicalAddress
) == 0)
370 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
372 if (MM_PTYPE(MmPageArray
[Start
].Flags
) != MM_PHYSICAL_PAGE_USED
)
374 DbgPrint("Dereferencing free page\n");
378 MmStats
.NrFreePages
++;
379 MmStats
.NrSystemPages
--;
381 MmPageArray
[Start
].ReferenceCount
--;
382 if (MmPageArray
[Start
].ReferenceCount
== 0)
384 RemoveEntryList(&MmPageArray
[Start
].ListEntry
);
385 if (MmPageArray
[Start
].LockCount
> 0)
387 DbgPrint("Freeing locked page\n");
390 if (MmPageArray
[Start
].Flags
!= MM_PHYSICAL_PAGE_USED
)
392 DbgPrint("Freeing page with flags %x\n",
393 MmPageArray
[Start
].Flags
);
396 MmPageArray
[Start
].Flags
= MM_PHYSICAL_PAGE_FREE
;
397 InsertTailList(&FreePageListHead
, &MmPageArray
[Start
].ListEntry
);
399 KeReleaseSpinLock(&PageListLock
, oldIrql
);
402 ULONG
MmGetLockCountPage(PVOID PhysicalAddress
)
404 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
408 DPRINT("MmGetLockCountPage(PhysicalAddress %x)\n", PhysicalAddress
);
410 if (((ULONG
)PhysicalAddress
) == 0)
415 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
417 if (MM_PTYPE(MmPageArray
[Start
].Flags
) != MM_PHYSICAL_PAGE_USED
)
419 DbgPrint("Getting lock count for free page\n");
423 LockCount
= MmPageArray
[Start
].LockCount
;
424 KeReleaseSpinLock(&PageListLock
, oldIrql
);
429 VOID
MmLockPage(PVOID PhysicalAddress
)
431 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
434 DPRINT("MmLockPage(PhysicalAddress %x)\n", PhysicalAddress
);
436 if (((ULONG
)PhysicalAddress
) == 0)
441 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
443 if (MM_PTYPE(MmPageArray
[Start
].Flags
) != MM_PHYSICAL_PAGE_USED
)
445 DbgPrint("Locking free page\n");
449 MmPageArray
[Start
].LockCount
++;
450 KeReleaseSpinLock(&PageListLock
, oldIrql
);
453 VOID
MmUnlockPage(PVOID PhysicalAddress
)
455 ULONG Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
458 DPRINT("MmUnlockPage(PhysicalAddress %x)\n", PhysicalAddress
);
460 if (((ULONG
)PhysicalAddress
) == 0)
465 KeAcquireSpinLock(&PageListLock
, &oldIrql
);
467 if (MM_PTYPE(MmPageArray
[Start
].Flags
) != MM_PHYSICAL_PAGE_USED
)
469 DbgPrint("Unlocking free page\n");
473 MmPageArray
[Start
].LockCount
--;
474 KeReleaseSpinLock(&PageListLock
, oldIrql
);
479 MmAllocPage(SWAPENTRY SavedSwapEntry
)
482 PLIST_ENTRY ListEntry
;
483 PPHYSICAL_PAGE PageDescriptor
;
485 DPRINT("MmAllocPage()\n");
487 ListEntry
= ExInterlockedRemoveHeadList(&FreePageListHead
,
489 DPRINT("ListEntry %x\n",ListEntry
);
490 if (ListEntry
== NULL
)
492 DPRINT("MmAllocPage(): Out of memory\n");
495 PageDescriptor
= CONTAINING_RECORD(ListEntry
, PHYSICAL_PAGE
, ListEntry
);
496 DPRINT("PageDescriptor %x\n",PageDescriptor
);
497 if (PageDescriptor
->Flags
!= MM_PHYSICAL_PAGE_FREE
)
499 DbgPrint("Got non-free page from freelist\n");
502 PageDescriptor
->Flags
= MM_PHYSICAL_PAGE_USED
;
503 PageDescriptor
->ReferenceCount
= 1;
504 PageDescriptor
->LockCount
= 0;
505 PageDescriptor
->SavedSwapEntry
= SavedSwapEntry
;
506 ExInterlockedInsertTailList(&UsedPageListHead
, ListEntry
,
509 DPRINT("PageDescriptor %x MmPageArray %x\n", PageDescriptor
, MmPageArray
);
510 offset
= (ULONG
)((ULONG
)PageDescriptor
- (ULONG
)MmPageArray
);
511 DPRINT("offset %x\n",offset
);
512 offset
= offset
/ sizeof(PHYSICAL_PAGE
) * PAGESIZE
;
513 DPRINT("offset %x\n",offset
);
515 MmStats
.NrSystemPages
++;
516 MmStats
.NrFreePages
--;
518 DPRINT("MmAllocPage() = %x\n",offset
);
519 return((PVOID
)offset
);
523 MmMustAllocPage(SWAPENTRY SavedSwapEntry
)
527 Page
= MmAllocPage(SavedSwapEntry
);
538 MmAllocPageMaybeSwap(SWAPENTRY SavedSwapEntry
)
542 Page
= MmAllocPage(SavedSwapEntry
);
545 MmWaitForFreePages();
546 Page
= MmAllocPage(SavedSwapEntry
);
552 MmWaitForPage(PVOID PhysicalAddress
)
557 Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
558 Status
= KeWaitForSingleObject(&MmPageArray
[Start
].Event
,
567 MmClearWaitPage(PVOID PhysicalAddress
)
571 Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
573 KeClearEvent(&MmPageArray
[Start
].Event
);
577 MmSetWaitPage(PVOID PhysicalAddress
)
581 Start
= (ULONG
)PhysicalAddress
/ PAGESIZE
;
583 KeSetEvent(&MmPageArray
[Start
].Event
,