3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
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 BOOLEAN
MmInitializeMemoryManager(VOID
)
48 BIOS_MEMORY_MAP BiosMemoryMap
[32];
49 ULONG BiosMemoryMapEntryCount
;
54 DbgPrint((DPRINT_MEMORY
, "Initializing Memory Manager.\n"));
56 RtlZeroMemory(BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
58 BiosMemoryMapEntryCount
= MachGetMemoryMap(BiosMemoryMap
, sizeof(BiosMemoryMap
) / sizeof(BIOS_MEMORY_MAP
));
61 // Dump the system memory map
62 if (BiosMemoryMapEntryCount
!= 0)
64 DbgPrint((DPRINT_MEMORY
, "System Memory Map (Base Address, Length, Type):\n"));
65 for (Index
=0; Index
<BiosMemoryMapEntryCount
; Index
++)
67 DbgPrint((DPRINT_MEMORY
, "%x%x\t %x%x\t %s\n", BiosMemoryMap
[Index
].BaseAddress
, BiosMemoryMap
[Index
].Length
, MmGetSystemMemoryMapTypeString(BiosMemoryMap
[Index
].Type
)));
72 // If we got the system memory map then fixup invalid entries
73 if (BiosMemoryMapEntryCount
!= 0)
75 MmFixupSystemMemoryMap(BiosMemoryMap
, &BiosMemoryMapEntryCount
);
78 // Find address for the page lookup table
79 TotalPagesInLookupTable
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, BiosMemoryMapEntryCount
);
80 PageLookupTableAddress
= MmFindLocationForPageLookupTable(BiosMemoryMap
, BiosMemoryMapEntryCount
);
81 LastFreePageHint
= TotalPagesInLookupTable
;
83 if (PageLookupTableAddress
== 0)
85 // If we get here then we probably couldn't
86 // find a contiguous chunk of memory big
87 // enough to hold the page lookup table
88 printf("Error initializing memory manager!\n");
92 // Initialize the page lookup table
93 MmInitPageLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
, BiosMemoryMap
, BiosMemoryMapEntryCount
);
94 MmUpdateLastFreePageHint(PageLookupTableAddress
, TotalPagesInLookupTable
);
96 FreePagesInLookupTable
= MmCountFreePagesInLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
);
98 DbgPrint((DPRINT_MEMORY
, "Memory Manager initialized. %d pages available.\n", FreePagesInLookupTable
));
103 PUCHAR
MmGetSystemMemoryMapTypeString(ULONG Type
)
107 for (Index
=1; Index
<MemoryTypeCount
; Index
++)
109 if (MemoryTypeArray
[Index
].Type
== Type
)
111 return MemoryTypeArray
[Index
].TypeString
;
115 return MemoryTypeArray
[0].TypeString
;
119 ULONG
MmGetPageNumberFromAddress(PVOID Address
)
121 return ((ULONG
)Address
) / MM_PAGE_SIZE
;
124 PVOID
MmGetEndAddressOfAnyMemory(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
126 ULONGLONG MaxStartAddressSoFar
;
127 ULONGLONG EndAddressOfMemory
;
130 MaxStartAddressSoFar
= 0;
131 EndAddressOfMemory
= 0;
132 for (Index
=0; Index
<MapCount
; Index
++)
134 if (MaxStartAddressSoFar
<= BiosMemoryMap
[Index
].BaseAddress
)
136 MaxStartAddressSoFar
= BiosMemoryMap
[Index
].BaseAddress
;
137 EndAddressOfMemory
= (MaxStartAddressSoFar
+ BiosMemoryMap
[Index
].Length
);
138 if (EndAddressOfMemory
> 0xFFFFFFFF)
140 EndAddressOfMemory
= 0xFFFFFFFF;
145 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returning 0x%x\n", (ULONG
)EndAddressOfMemory
));
147 return (PVOID
)(ULONG
)EndAddressOfMemory
;
150 ULONG
MmGetAddressablePageCountIncludingHoles(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
153 ULONGLONG EndAddress
;
155 EndAddress
= (ULONGLONG
)(ULONG
)MmGetEndAddressOfAnyMemory(BiosMemoryMap
, MapCount
);
157 // Since MmGetEndAddressOfAnyMemory() won't
158 // return addresses higher than 0xFFFFFFFF
159 // then we need to adjust the end address
160 // to 0x100000000 so we don't get an
162 if (EndAddress
>= 0xFFFFFFFF)
164 EndAddress
= 0x100000000LL
;
166 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returned 0xFFFFFFFF, correcting to be 0x100000000.\n"));
169 PageCount
= (EndAddress
/ MM_PAGE_SIZE
);
171 DbgPrint((DPRINT_MEMORY
, "MmGetAddressablePageCountIncludingHoles() returning %d\n", PageCount
));
176 PVOID
MmFindLocationForPageLookupTable(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
178 ULONG TotalPageCount
;
179 ULONG PageLookupTableSize
;
180 PVOID PageLookupTableMemAddress
;
182 BIOS_MEMORY_MAP TempBiosMemoryMap
[32];
184 TotalPageCount
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, MapCount
);
185 PageLookupTableSize
= TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
);
186 PageLookupTableMemAddress
= 0;
188 RtlCopyMemory(TempBiosMemoryMap
, BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
189 MmSortBiosMemoryMap(TempBiosMemoryMap
, MapCount
);
191 for (Index
=(MapCount
-1); Index
>=0; Index
--)
193 // If this is usable memory with a big enough length
194 // then we'll put our page lookup table here
195 if (TempBiosMemoryMap
[Index
].Type
== BiosMemoryUsable
&& TempBiosMemoryMap
[Index
].Length
>= PageLookupTableSize
)
197 PageLookupTableMemAddress
= (PVOID
)(ULONG
)(TempBiosMemoryMap
[Index
].BaseAddress
+ (TempBiosMemoryMap
[Index
].Length
- PageLookupTableSize
));
202 DbgPrint((DPRINT_MEMORY
, "MmFindLocationForPageLookupTable() returning 0x%x\n", PageLookupTableMemAddress
));
204 return PageLookupTableMemAddress
;
207 VOID
MmSortBiosMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
211 BIOS_MEMORY_MAP TempMapItem
;
213 // Loop once for each entry in the memory map minus one
214 // On each loop iteration go through and sort the memory map
215 for (LoopCount
=0; LoopCount
<(MapCount
-1); LoopCount
++)
217 for (Index
=0; Index
<(MapCount
-1); Index
++)
219 if (BiosMemoryMap
[Index
].BaseAddress
> BiosMemoryMap
[Index
+1].BaseAddress
)
221 TempMapItem
= BiosMemoryMap
[Index
];
222 BiosMemoryMap
[Index
] = BiosMemoryMap
[Index
+1];
223 BiosMemoryMap
[Index
+1] = TempMapItem
;
229 VOID
MmInitPageLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
, PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
231 ULONG MemoryMapStartPage
;
232 ULONG MemoryMapEndPage
;
233 ULONG MemoryMapPageCount
;
234 ULONG MemoryMapPageAllocated
;
235 ULONG PageLookupTableStartPage
;
236 ULONG PageLookupTablePageCount
;
239 DbgPrint((DPRINT_MEMORY
, "MmInitPageLookupTable()\n"));
241 // Mark every page as allocated initially
242 // We will go through and mark pages again according to the memory map
243 // But this will mark any holes not described in the map as allocated
244 MmMarkPagesInLookupTable(PageLookupTable
, 0, TotalPageCount
, 1);
246 for (Index
=0; Index
<MapCount
; Index
++)
248 MemoryMapStartPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG
)BiosMemoryMap
[Index
].BaseAddress
);
249 MemoryMapEndPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG
)(BiosMemoryMap
[Index
].BaseAddress
+ BiosMemoryMap
[Index
].Length
- 1));
250 MemoryMapPageCount
= (MemoryMapEndPage
- MemoryMapStartPage
) + 1;
251 MemoryMapPageAllocated
= (BiosMemoryMap
[Index
].Type
== BiosMemoryUsable
) ? LoaderFree
: LoaderFirmwarePermanent
;/*BiosMemoryMap[Index].Type*/;
252 DbgPrint((DPRINT_MEMORY
, "Marking pages as type %d: StartPage: %d PageCount: %d\n", MemoryMapPageAllocated
, MemoryMapStartPage
, MemoryMapPageCount
));
253 MmMarkPagesInLookupTable(PageLookupTable
, MemoryMapStartPage
, MemoryMapPageCount
, MemoryMapPageAllocated
);
256 // Mark the low memory region below 1MB as reserved (256 pages in region)
257 DbgPrint((DPRINT_MEMORY
, "Marking the low 1MB region as reserved.\n"));
258 MmMarkPagesInLookupTable(PageLookupTable
, 0, 256, LoaderFirmwarePermanent
);
260 // Mark the pages that the lookup table occupies as reserved
261 PageLookupTableStartPage
= MmGetPageNumberFromAddress(PageLookupTable
);
262 PageLookupTablePageCount
= MmGetPageNumberFromAddress((PVOID
)((ULONG_PTR
)PageLookupTable
+ ROUND_UP(TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
), MM_PAGE_SIZE
))) - PageLookupTableStartPage
;
263 DbgPrint((DPRINT_MEMORY
, "Marking the page lookup table pages as reserved StartPage: %d PageCount: %d\n", PageLookupTableStartPage
, PageLookupTablePageCount
));
264 MmMarkPagesInLookupTable(PageLookupTable
, PageLookupTableStartPage
, PageLookupTablePageCount
, LoaderFirmwareTemporary
);
267 VOID
MmMarkPagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
, TYPE_OF_MEMORY PageAllocated
)
269 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
272 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
274 if ((Index
<= (StartPage
+ 16)) || (Index
>= (StartPage
+PageCount
-16)))
276 DbgPrint((DPRINT_MEMORY
, "Index = %d StartPage = %d PageCount = %d\n", Index
, StartPage
, PageCount
));
278 RealPageLookupTable
[Index
].PageAllocated
= PageAllocated
;
279 RealPageLookupTable
[Index
].PageAllocationLength
= (PageAllocated
!= LoaderFree
) ? 1 : 0;
281 DbgPrint((DPRINT_MEMORY
, "MmMarkPagesInLookupTable() Done\n"));
284 VOID
MmAllocatePagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
)
286 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
289 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
291 RealPageLookupTable
[Index
].PageAllocated
= LoaderSystemCode
;
292 RealPageLookupTable
[Index
].PageAllocationLength
= (Index
== StartPage
) ? PageCount
: 0;
296 ULONG
MmCountFreePagesInLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
)
298 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
303 for (Index
=0; Index
<TotalPageCount
; Index
++)
305 if (RealPageLookupTable
[Index
].PageAllocated
== LoaderFree
)
311 return FreePageCount
;
314 ULONG
MmFindAvailablePages(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
, BOOLEAN FromEnd
)
316 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
317 ULONG AvailablePagesSoFar
;
320 if (LastFreePageHint
> TotalPageCount
)
322 LastFreePageHint
= TotalPageCount
;
325 AvailablePagesSoFar
= 0;
328 /* Allocate "high" (from end) pages */
329 for (Index
=LastFreePageHint
-1; Index
>0; Index
--)
331 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
333 AvailablePagesSoFar
= 0;
338 AvailablePagesSoFar
++;
341 if (AvailablePagesSoFar
>= PagesNeeded
)
349 DbgPrint((DPRINT_MEMORY
, "Alloc low memory, LastFreePageHint %d, TPC %d\n", LastFreePageHint
, TotalPageCount
));
350 /* Allocate "low" pages */
351 for (Index
=1; Index
< LastFreePageHint
; Index
++)
353 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
355 AvailablePagesSoFar
= 0;
360 AvailablePagesSoFar
++;
363 if (AvailablePagesSoFar
>= PagesNeeded
)
365 return Index
- AvailablePagesSoFar
+ 1;
373 ULONG
MmFindAvailablePagesBeforePage(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
, ULONG LastPage
)
375 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
376 ULONG AvailablePagesSoFar
;
379 if (LastPage
> TotalPageCount
)
381 return MmFindAvailablePages(PageLookupTable
, TotalPageCount
, PagesNeeded
, TRUE
);
384 AvailablePagesSoFar
= 0;
385 for (Index
=LastPage
-1; Index
>0; Index
--)
387 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
389 AvailablePagesSoFar
= 0;
394 AvailablePagesSoFar
++;
397 if (AvailablePagesSoFar
>= PagesNeeded
)
406 VOID
MmFixupSystemMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG
* MapCount
)
411 // Loop through each entry in the array
412 for (Index
=0; Index
<*MapCount
; Index
++)
414 // If the entry type isn't usable then remove
415 // it from the memory map (this will help reduce
416 // the size of our lookup table)
417 if (BiosMemoryMap
[Index
].Type
!= BiosMemoryUsable
)
419 // Slide every entry after this down one
420 for (Index2
=Index
; Index2
<(*MapCount
- 1); Index2
++)
422 BiosMemoryMap
[Index2
] = BiosMemoryMap
[Index2
+ 1];
430 VOID
MmUpdateLastFreePageHint(PVOID PageLookupTable
, ULONG TotalPageCount
)
432 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
435 for (Index
=TotalPageCount
-1; Index
>0; Index
--)
437 if (RealPageLookupTable
[Index
].PageAllocated
== LoaderFree
)
439 LastFreePageHint
= Index
+ 1;
445 BOOLEAN
MmAreMemoryPagesAvailable(PVOID PageLookupTable
, ULONG TotalPageCount
, PVOID PageAddress
, ULONG PageCount
)
447 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
451 StartPage
= MmGetPageNumberFromAddress(PageAddress
);
453 // Make sure they aren't trying to go past the
454 // end of availabe memory
455 if ((StartPage
+ PageCount
) > TotalPageCount
)
460 for (Index
=StartPage
; Index
<(StartPage
+ PageCount
); Index
++)
462 // If this page is allocated then there obviously isn't
463 // memory availabe so return FALSE
464 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)