3 * Copyright (C) 2006-2008 Aleksey Bragin <aleksey@reactos.org>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 } FREELDR_MEMORY_TYPE
, *PFREELDR_MEMORY_TYPE
;
30 ULONG MemoryTypeCount
= 5;
31 FREELDR_MEMORY_TYPE MemoryTypeArray
[] =
33 { 0, "Unknown Memory" },
34 { BiosMemoryUsable
, "Usable Memory" },
35 { BiosMemoryReserved
, "Reserved Memory" },
36 { BiosMemoryAcpiReclaim
, "ACPI Reclaim Memory" },
37 { BiosMemoryAcpiNvs
, "ACPI NVS Memory" },
41 PVOID PageLookupTableAddress
= NULL
;
42 ULONG TotalPagesInLookupTable
= 0;
43 ULONG FreePagesInLookupTable
= 0;
44 ULONG LastFreePageHint
= 0;
46 extern ULONG_PTR MmHeapPointer
;
47 extern ULONG_PTR MmHeapStart
;
49 BOOLEAN
MmInitializeMemoryManager(VOID
)
51 BIOS_MEMORY_MAP BiosMemoryMap
[32];
52 ULONG BiosMemoryMapEntryCount
;
57 DbgPrint((DPRINT_MEMORY
, "Initializing Memory Manager.\n"));
59 RtlZeroMemory(BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
61 BiosMemoryMapEntryCount
= MachGetMemoryMap(BiosMemoryMap
, sizeof(BiosMemoryMap
) / sizeof(BIOS_MEMORY_MAP
));
64 // Dump the system memory map
65 if (BiosMemoryMapEntryCount
!= 0)
67 DbgPrint((DPRINT_MEMORY
, "System Memory Map (Base Address, Length, Type):\n"));
68 for (Index
=0; Index
<BiosMemoryMapEntryCount
; Index
++)
70 DbgPrint((DPRINT_MEMORY
, "%x%x\t %x%x\t %s\n", BiosMemoryMap
[Index
].BaseAddress
, BiosMemoryMap
[Index
].Length
, MmGetSystemMemoryMapTypeString(BiosMemoryMap
[Index
].Type
)));
75 // If we got the system memory map then fixup invalid entries
76 if (BiosMemoryMapEntryCount
!= 0)
78 MmFixupSystemMemoryMap(BiosMemoryMap
, &BiosMemoryMapEntryCount
);
81 // Find address for the page lookup table
82 TotalPagesInLookupTable
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, BiosMemoryMapEntryCount
);
83 PageLookupTableAddress
= MmFindLocationForPageLookupTable(BiosMemoryMap
, BiosMemoryMapEntryCount
);
84 LastFreePageHint
= TotalPagesInLookupTable
;
86 if (PageLookupTableAddress
== 0)
88 // If we get here then we probably couldn't
89 // find a contiguous chunk of memory big
90 // enough to hold the page lookup table
91 printf("Error initializing memory manager!\n");
95 // Initialize the page lookup table
96 MmInitPageLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
, BiosMemoryMap
, BiosMemoryMapEntryCount
);
97 MmUpdateLastFreePageHint(PageLookupTableAddress
, TotalPagesInLookupTable
);
99 // Add machine-dependent stuff
100 #if defined (__i386__) || defined (_M_AMD64)
101 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x00, 1, LoaderFirmwarePermanent
); // realmode int vectors
102 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x01, 7, LoaderFirmwareTemporary
); // freeldr stack + cmdline
103 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x08, 0x70, LoaderLoadedProgram
); // freeldr image (roughly max. 0x64 pages)
104 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x78, 8, LoaderOsloaderStack
); // prot mode stack. BIOSCALLBUFFER
105 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x80, 0x10, LoaderOsloaderHeap
); // File system read buffer. FILESYSBUFFER
106 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x90, 0x10, LoaderOsloaderHeap
); // Disk read buffer for int 13h. DISKREADBUFFER
107 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0xA0, 0x60, LoaderFirmwarePermanent
); // ROM / Video
108 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0xFFF, 1, LoaderSpecialMemory
); // unusable memory
109 #if defined (_M_AMD64)
110 MmMarkPagesInLookupTable(PageLookupTableAddress
, PML4_PAGENUM
, PML4_PAGES
, LoaderSpecialMemory
); // the page table
113 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x00, 1, LoaderFirmwarePermanent
); // arm exception handlers
114 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x01, 7, LoaderFirmwareTemporary
); // arm board block + freeldr stack + cmdline
115 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x08, 0x70, LoaderLoadedProgram
); // freeldr image (roughly max. 0x64 pages)
118 FreePagesInLookupTable
= MmCountFreePagesInLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
);
120 MmInitializeHeap(PageLookupTableAddress
);
122 DbgPrint((DPRINT_MEMORY
, "Memory Manager initialized. %d pages available.\n", FreePagesInLookupTable
));
126 VOID
MmInitializeHeap(PVOID PageLookupTable
)
131 // HACK: Make it so it doesn't overlap kernel space
132 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x100, 0xFF, LoaderSystemCode
);
134 // Find contigious memory block for HEAP:STACK
135 PagesNeeded
= HEAP_PAGES
+ STACK_PAGES
;
136 HeapStart
= MmFindAvailablePages(PageLookupTable
, TotalPagesInLookupTable
, PagesNeeded
, FALSE
);
139 MmMarkPagesInLookupTable(PageLookupTableAddress
, 0x100, 0xFF, LoaderFree
);
143 UiMessageBox("Critical error: Can't allocate heap!");
148 bpool(HeapStart
<< MM_PAGE_SHIFT
, PagesNeeded
<< MM_PAGE_SHIFT
);
150 // Mark those pages as used
151 MmMarkPagesInLookupTable(PageLookupTableAddress
, HeapStart
, PagesNeeded
, LoaderOsloaderHeap
);
153 DbgPrint((DPRINT_MEMORY
, "Heap initialized, base 0x%08x, pages %d\n", (HeapStart
<< MM_PAGE_SHIFT
), PagesNeeded
));
157 PUCHAR
MmGetSystemMemoryMapTypeString(ULONG Type
)
161 for (Index
=1; Index
<MemoryTypeCount
; Index
++)
163 if (MemoryTypeArray
[Index
].Type
== Type
)
165 return MemoryTypeArray
[Index
].TypeString
;
169 return MemoryTypeArray
[0].TypeString
;
173 ULONG
MmGetPageNumberFromAddress(PVOID Address
)
175 return ((ULONG_PTR
)Address
) / MM_PAGE_SIZE
;
178 PVOID
MmGetEndAddressOfAnyMemory(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
180 ULONGLONG MaxStartAddressSoFar
;
181 ULONGLONG EndAddressOfMemory
;
184 MaxStartAddressSoFar
= 0;
185 EndAddressOfMemory
= 0;
186 for (Index
=0; Index
<MapCount
; Index
++)
188 if (MaxStartAddressSoFar
<= BiosMemoryMap
[Index
].BaseAddress
)
190 MaxStartAddressSoFar
= BiosMemoryMap
[Index
].BaseAddress
;
191 EndAddressOfMemory
= (MaxStartAddressSoFar
+ BiosMemoryMap
[Index
].Length
);
192 if (EndAddressOfMemory
> 0xFFFFFFFF)
194 EndAddressOfMemory
= 0xFFFFFFFF;
199 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returning 0x%x\n", (ULONG
)EndAddressOfMemory
));
201 return (PVOID
)(ULONG_PTR
)EndAddressOfMemory
;
204 ULONG
MmGetAddressablePageCountIncludingHoles(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
207 ULONGLONG EndAddress
;
209 EndAddress
= (ULONGLONG
)(ULONG_PTR
)MmGetEndAddressOfAnyMemory(BiosMemoryMap
, MapCount
);
211 // Since MmGetEndAddressOfAnyMemory() won't
212 // return addresses higher than 0xFFFFFFFF
213 // then we need to adjust the end address
214 // to 0x100000000 so we don't get an
216 if (EndAddress
>= 0xFFFFFFFF)
218 EndAddress
= 0x100000000LL
;
220 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returned 0xFFFFFFFF, correcting to be 0x100000000.\n"));
223 PageCount
= (EndAddress
/ MM_PAGE_SIZE
);
225 DbgPrint((DPRINT_MEMORY
, "MmGetAddressablePageCountIncludingHoles() returning %d\n", PageCount
));
230 PVOID
MmFindLocationForPageLookupTable(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
232 ULONG TotalPageCount
;
233 ULONG PageLookupTableSize
;
234 PVOID PageLookupTableMemAddress
;
236 BIOS_MEMORY_MAP TempBiosMemoryMap
[32];
238 TotalPageCount
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, MapCount
);
239 PageLookupTableSize
= TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
);
240 PageLookupTableMemAddress
= 0;
242 RtlCopyMemory(TempBiosMemoryMap
, BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
243 MmSortBiosMemoryMap(TempBiosMemoryMap
, MapCount
);
245 // Find a place, starting from the highest memory
246 // (thus leaving low memory for kernel/drivers)
247 for (Index
=(MapCount
-1); Index
>=0; Index
--)
249 // If this is usable memory with a big enough length
250 // then we'll put our page lookup table here
252 // skip if this is not usable region
253 if (TempBiosMemoryMap
[Index
].Type
!= BiosMemoryUsable
)
256 if (TempBiosMemoryMap
[Index
].Length
>= PageLookupTableSize
)
258 PageLookupTableMemAddress
= (PVOID
)(ULONG_PTR
)
259 (TempBiosMemoryMap
[Index
].BaseAddress
+ (TempBiosMemoryMap
[Index
].Length
- PageLookupTableSize
));
264 DbgPrint((DPRINT_MEMORY
, "MmFindLocationForPageLookupTable() returning 0x%x\n", PageLookupTableMemAddress
));
266 return PageLookupTableMemAddress
;
269 VOID
MmSortBiosMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
273 BIOS_MEMORY_MAP TempMapItem
;
275 // Loop once for each entry in the memory map minus one
276 // On each loop iteration go through and sort the memory map
277 for (LoopCount
=0; LoopCount
<(MapCount
-1); LoopCount
++)
279 for (Index
=0; Index
<(MapCount
-1); Index
++)
281 if (BiosMemoryMap
[Index
].BaseAddress
> BiosMemoryMap
[Index
+1].BaseAddress
)
283 TempMapItem
= BiosMemoryMap
[Index
];
284 BiosMemoryMap
[Index
] = BiosMemoryMap
[Index
+1];
285 BiosMemoryMap
[Index
+1] = TempMapItem
;
291 VOID
MmInitPageLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
, PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
293 ULONG MemoryMapStartPage
;
294 ULONG MemoryMapEndPage
;
295 ULONG MemoryMapPageCount
;
296 ULONG MemoryMapPageAllocated
;
297 ULONG PageLookupTableStartPage
;
298 ULONG PageLookupTablePageCount
;
301 DbgPrint((DPRINT_MEMORY
, "MmInitPageLookupTable()\n"));
303 // Mark every page as allocated initially
304 // We will go through and mark pages again according to the memory map
305 // But this will mark any holes not described in the map as allocated
306 MmMarkPagesInLookupTable(PageLookupTable
, 0, TotalPageCount
, LoaderFirmwarePermanent
);
308 for (Index
=0; Index
<MapCount
; Index
++)
310 MemoryMapStartPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG_PTR
)BiosMemoryMap
[Index
].BaseAddress
);
311 MemoryMapEndPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG_PTR
)(BiosMemoryMap
[Index
].BaseAddress
+ BiosMemoryMap
[Index
].Length
- 1));
312 MemoryMapPageCount
= (MemoryMapEndPage
- MemoryMapStartPage
) + 1;
314 switch (BiosMemoryMap
[Index
].Type
)
316 case BiosMemoryUsable
:
317 MemoryMapPageAllocated
= LoaderFree
;
320 case BiosMemoryAcpiReclaim
:
321 case BiosMemoryAcpiNvs
:
322 MemoryMapPageAllocated
= LoaderSpecialMemory
;
326 MemoryMapPageAllocated
= LoaderSpecialMemory
;
328 DbgPrint((DPRINT_MEMORY
, "Marking pages as type %d: StartPage: %d PageCount: %d\n", MemoryMapPageAllocated
, MemoryMapStartPage
, MemoryMapPageCount
));
329 MmMarkPagesInLookupTable(PageLookupTable
, MemoryMapStartPage
, MemoryMapPageCount
, MemoryMapPageAllocated
);
332 // Mark the pages that the lookup table occupies as reserved
333 PageLookupTableStartPage
= MmGetPageNumberFromAddress(PageLookupTable
);
334 PageLookupTablePageCount
= MmGetPageNumberFromAddress((PVOID
)((ULONG_PTR
)PageLookupTable
+ ROUND_UP(TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
), MM_PAGE_SIZE
))) - PageLookupTableStartPage
;
335 DbgPrint((DPRINT_MEMORY
, "Marking the page lookup table pages as reserved StartPage: %d PageCount: %d\n", PageLookupTableStartPage
, PageLookupTablePageCount
));
336 MmMarkPagesInLookupTable(PageLookupTable
, PageLookupTableStartPage
, PageLookupTablePageCount
, LoaderFirmwareTemporary
);
339 VOID
MmMarkPagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
, TYPE_OF_MEMORY PageAllocated
)
341 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
344 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
347 if ((Index
<= (StartPage
+ 16)) || (Index
>= (StartPage
+PageCount
-16)))
349 DbgPrint((DPRINT_MEMORY
, "Index = %d StartPage = %d PageCount = %d\n", Index
, StartPage
, PageCount
));
352 RealPageLookupTable
[Index
].PageAllocated
= PageAllocated
;
353 RealPageLookupTable
[Index
].PageAllocationLength
= (PageAllocated
!= LoaderFree
) ? 1 : 0;
355 DbgPrint((DPRINT_MEMORY
, "MmMarkPagesInLookupTable() Done\n"));
358 VOID
MmAllocatePagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
, TYPE_OF_MEMORY MemoryType
)
360 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
363 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
365 RealPageLookupTable
[Index
].PageAllocated
= MemoryType
;
366 RealPageLookupTable
[Index
].PageAllocationLength
= (Index
== StartPage
) ? PageCount
: 0;
370 ULONG
MmCountFreePagesInLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
)
372 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
377 for (Index
=0; Index
<TotalPageCount
; Index
++)
379 if (RealPageLookupTable
[Index
].PageAllocated
== LoaderFree
)
385 return FreePageCount
;
388 ULONG
MmFindAvailablePages(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
, BOOLEAN FromEnd
)
390 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
391 ULONG AvailablePagesSoFar
;
394 if (LastFreePageHint
> TotalPageCount
)
396 LastFreePageHint
= TotalPageCount
;
399 AvailablePagesSoFar
= 0;
402 /* Allocate "high" (from end) pages */
403 for (Index
=LastFreePageHint
-1; Index
>0; Index
--)
405 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
407 AvailablePagesSoFar
= 0;
412 AvailablePagesSoFar
++;
415 if (AvailablePagesSoFar
>= PagesNeeded
)
423 DbgPrint((DPRINT_MEMORY
, "Alloc low memory, LastFreePageHint %d, TPC %d\n", LastFreePageHint
, TotalPageCount
));
424 /* Allocate "low" pages */
425 for (Index
=1; Index
< LastFreePageHint
; Index
++)
427 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
429 AvailablePagesSoFar
= 0;
434 AvailablePagesSoFar
++;
437 if (AvailablePagesSoFar
>= PagesNeeded
)
439 return Index
- AvailablePagesSoFar
+ 1;
447 ULONG
MmFindAvailablePagesBeforePage(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
, ULONG LastPage
)
449 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
450 ULONG AvailablePagesSoFar
;
453 if (LastPage
> TotalPageCount
)
455 return MmFindAvailablePages(PageLookupTable
, TotalPageCount
, PagesNeeded
, TRUE
);
458 AvailablePagesSoFar
= 0;
459 for (Index
=LastPage
-1; Index
>0; Index
--)
461 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
463 AvailablePagesSoFar
= 0;
468 AvailablePagesSoFar
++;
471 if (AvailablePagesSoFar
>= PagesNeeded
)
480 VOID
MmFixupSystemMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG
* MapCount
)
485 // Loop through each entry in the array
486 for (Index
=0; Index
<*MapCount
; Index
++)
488 // If the entry type isn't usable then remove
489 // it from the memory map (this will help reduce
490 // the size of our lookup table)
491 if (BiosMemoryMap
[Index
].Type
!= BiosMemoryUsable
)
493 // Slide every entry after this down one
494 for (Index2
=Index
; Index2
<(*MapCount
- 1); Index2
++)
496 BiosMemoryMap
[Index2
] = BiosMemoryMap
[Index2
+ 1];
504 VOID
MmUpdateLastFreePageHint(PVOID PageLookupTable
, ULONG TotalPageCount
)
506 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
509 for (Index
=TotalPageCount
-1; Index
>0; Index
--)
511 if (RealPageLookupTable
[Index
].PageAllocated
== LoaderFree
)
513 LastFreePageHint
= Index
+ 1;
519 BOOLEAN
MmAreMemoryPagesAvailable(PVOID PageLookupTable
, ULONG TotalPageCount
, PVOID PageAddress
, ULONG PageCount
)
521 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
525 StartPage
= MmGetPageNumberFromAddress(PageAddress
);
527 // Make sure they aren't trying to go past the
528 // end of availabe memory
529 if ((StartPage
+ PageCount
) > TotalPageCount
)
534 for (Index
=StartPage
; Index
<(StartPage
+ PageCount
); Index
++)
536 // If this page is allocated then there obviously isn't
537 // memory availabe so return FALSE
538 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)