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.
30 } FREELDR_MEMORY_TYPE
, *PFREELDR_MEMORY_TYPE
;
32 ULONG MemoryTypeCount
= 5;
33 FREELDR_MEMORY_TYPE MemoryTypeArray
[] =
35 { 0, "Unknown Memory" },
36 { BiosMemoryUsable
, "Usable Memory" },
37 { BiosMemoryReserved
, "Reserved Memory" },
38 { BiosMemoryAcpiReclaim
, "ACPI Reclaim Memory" },
39 { BiosMemoryAcpiNvs
, "ACPI NVS Memory" },
43 PVOID PageLookupTableAddress
= NULL
;
44 ULONG TotalPagesInLookupTable
= 0;
45 ULONG FreePagesInLookupTable
= 0;
46 ULONG LastFreePageHint
= 0;
48 BOOLEAN
MmInitializeMemoryManager(VOID
)
50 BIOS_MEMORY_MAP BiosMemoryMap
[32];
51 ULONG BiosMemoryMapEntryCount
;
56 DbgPrint((DPRINT_MEMORY
, "Initializing Memory Manager.\n"));
58 RtlZeroMemory(BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
60 BiosMemoryMapEntryCount
= MachGetMemoryMap(BiosMemoryMap
, sizeof(BiosMemoryMap
) / sizeof(BIOS_MEMORY_MAP
));
63 // Dump the system memory map
64 if (BiosMemoryMapEntryCount
!= 0)
66 DbgPrint((DPRINT_MEMORY
, "System Memory Map (Base Address, Length, Type):\n"));
67 for (Index
=0; Index
<BiosMemoryMapEntryCount
; Index
++)
69 DbgPrint((DPRINT_MEMORY
, "%x%x\t %x%x\t %s\n", BiosMemoryMap
[Index
].BaseAddress
, BiosMemoryMap
[Index
].Length
, MmGetSystemMemoryMapTypeString(BiosMemoryMap
[Index
].Type
)));
74 // If we got the system memory map then fixup invalid entries
75 if (BiosMemoryMapEntryCount
!= 0)
77 MmFixupSystemMemoryMap(BiosMemoryMap
, &BiosMemoryMapEntryCount
);
80 // Find address for the page lookup table
81 TotalPagesInLookupTable
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, BiosMemoryMapEntryCount
);
82 PageLookupTableAddress
= MmFindLocationForPageLookupTable(BiosMemoryMap
, BiosMemoryMapEntryCount
);
83 LastFreePageHint
= TotalPagesInLookupTable
;
85 if (PageLookupTableAddress
== 0)
87 // If we get here then we probably couldn't
88 // find a contiguous chunk of memory big
89 // enough to hold the page lookup table
90 printf("Error initializing memory manager!\n");
94 // Initialize the page lookup table
95 MmInitPageLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
, BiosMemoryMap
, BiosMemoryMapEntryCount
);
96 MmUpdateLastFreePageHint(PageLookupTableAddress
, TotalPagesInLookupTable
);
98 FreePagesInLookupTable
= MmCountFreePagesInLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
);
100 DbgPrint((DPRINT_MEMORY
, "Memory Manager initialized. %d pages available.\n", FreePagesInLookupTable
));
105 PUCHAR
MmGetSystemMemoryMapTypeString(ULONG Type
)
109 for (Index
=1; Index
<MemoryTypeCount
; Index
++)
111 if (MemoryTypeArray
[Index
].Type
== Type
)
113 return MemoryTypeArray
[Index
].TypeString
;
117 return MemoryTypeArray
[0].TypeString
;
121 ULONG
MmGetPageNumberFromAddress(PVOID Address
)
123 return ((ULONG
)Address
) / MM_PAGE_SIZE
;
126 PVOID
MmGetEndAddressOfAnyMemory(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
128 ULONGLONG MaxStartAddressSoFar
;
129 ULONGLONG EndAddressOfMemory
;
132 MaxStartAddressSoFar
= 0;
133 EndAddressOfMemory
= 0;
134 for (Index
=0; Index
<MapCount
; Index
++)
136 if (MaxStartAddressSoFar
<= BiosMemoryMap
[Index
].BaseAddress
)
138 MaxStartAddressSoFar
= BiosMemoryMap
[Index
].BaseAddress
;
139 EndAddressOfMemory
= (MaxStartAddressSoFar
+ BiosMemoryMap
[Index
].Length
);
140 if (EndAddressOfMemory
> 0xFFFFFFFF)
142 EndAddressOfMemory
= 0xFFFFFFFF;
147 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returning 0x%x\n", (ULONG
)EndAddressOfMemory
));
149 return (PVOID
)(ULONG
)EndAddressOfMemory
;
152 ULONG
MmGetAddressablePageCountIncludingHoles(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
155 ULONGLONG EndAddress
;
157 EndAddress
= (ULONGLONG
)(ULONG
)MmGetEndAddressOfAnyMemory(BiosMemoryMap
, MapCount
);
159 // Since MmGetEndAddressOfAnyMemory() won't
160 // return addresses higher than 0xFFFFFFFF
161 // then we need to adjust the end address
162 // to 0x100000000 so we don't get an
164 if (EndAddress
>= 0xFFFFFFFF)
166 EndAddress
= 0x100000000LL
;
168 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returned 0xFFFFFFFF, correcting to be 0x100000000.\n"));
171 PageCount
= (EndAddress
/ MM_PAGE_SIZE
);
173 DbgPrint((DPRINT_MEMORY
, "MmGetAddressablePageCountIncludingHoles() returning %d\n", PageCount
));
178 PVOID
MmFindLocationForPageLookupTable(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
180 ULONG TotalPageCount
;
181 ULONG PageLookupTableSize
;
182 PVOID PageLookupTableMemAddress
;
184 BIOS_MEMORY_MAP TempBiosMemoryMap
[32];
186 TotalPageCount
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, MapCount
);
187 PageLookupTableSize
= TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
);
188 PageLookupTableMemAddress
= 0;
190 RtlCopyMemory(TempBiosMemoryMap
, BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
191 MmSortBiosMemoryMap(TempBiosMemoryMap
, MapCount
);
193 for (Index
=(MapCount
-1); Index
>=0; Index
--)
195 // If this is usable memory with a big enough length
196 // then we'll put our page lookup table here
197 if (TempBiosMemoryMap
[Index
].Type
== BiosMemoryUsable
&& TempBiosMemoryMap
[Index
].Length
>= PageLookupTableSize
)
199 PageLookupTableMemAddress
= (PVOID
)(ULONG
)(TempBiosMemoryMap
[Index
].BaseAddress
+ (TempBiosMemoryMap
[Index
].Length
- PageLookupTableSize
));
204 DbgPrint((DPRINT_MEMORY
, "MmFindLocationForPageLookupTable() returning 0x%x\n", PageLookupTableMemAddress
));
206 return PageLookupTableMemAddress
;
209 VOID
MmSortBiosMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
213 BIOS_MEMORY_MAP TempMapItem
;
215 // Loop once for each entry in the memory map minus one
216 // On each loop iteration go through and sort the memory map
217 for (LoopCount
=0; LoopCount
<(MapCount
-1); LoopCount
++)
219 for (Index
=0; Index
<(MapCount
-1); Index
++)
221 if (BiosMemoryMap
[Index
].BaseAddress
> BiosMemoryMap
[Index
+1].BaseAddress
)
223 TempMapItem
= BiosMemoryMap
[Index
];
224 BiosMemoryMap
[Index
] = BiosMemoryMap
[Index
+1];
225 BiosMemoryMap
[Index
+1] = TempMapItem
;
231 VOID
MmInitPageLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
, PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
233 ULONG MemoryMapStartPage
;
234 ULONG MemoryMapEndPage
;
235 ULONG MemoryMapPageCount
;
236 ULONG MemoryMapPageAllocated
;
237 ULONG PageLookupTableStartPage
;
238 ULONG PageLookupTablePageCount
;
241 DbgPrint((DPRINT_MEMORY
, "MmInitPageLookupTable()\n"));
243 // Mark every page as allocated initially
244 // We will go through and mark pages again according to the memory map
245 // But this will mark any holes not described in the map as allocated
246 MmMarkPagesInLookupTable(PageLookupTable
, 0, TotalPageCount
, 1);
248 for (Index
=0; Index
<MapCount
; Index
++)
250 MemoryMapStartPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG
)BiosMemoryMap
[Index
].BaseAddress
);
251 MemoryMapEndPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG
)(BiosMemoryMap
[Index
].BaseAddress
+ BiosMemoryMap
[Index
].Length
- 1));
252 MemoryMapPageCount
= (MemoryMapEndPage
- MemoryMapStartPage
) + 1;
253 MemoryMapPageAllocated
= (BiosMemoryMap
[Index
].Type
== BiosMemoryUsable
) ? LoaderFree
: LoaderFirmwarePermanent
;/*BiosMemoryMap[Index].Type*/;
254 DbgPrint((DPRINT_MEMORY
, "Marking pages as type %d: StartPage: %d PageCount: %d\n", MemoryMapPageAllocated
, MemoryMapStartPage
, MemoryMapPageCount
));
255 MmMarkPagesInLookupTable(PageLookupTable
, MemoryMapStartPage
, MemoryMapPageCount
, MemoryMapPageAllocated
);
258 // Mark the low memory region below 1MB as reserved (256 pages in region)
259 DbgPrint((DPRINT_MEMORY
, "Marking the low 1MB region as reserved.\n"));
260 MmMarkPagesInLookupTable(PageLookupTable
, 0, 256, LoaderFirmwarePermanent
);
262 // Mark the pages that the lookup table occupies as reserved
263 PageLookupTableStartPage
= MmGetPageNumberFromAddress(PageLookupTable
);
264 PageLookupTablePageCount
= MmGetPageNumberFromAddress((PVOID
)((ULONG_PTR
)PageLookupTable
+ ROUND_UP(TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
), MM_PAGE_SIZE
))) - PageLookupTableStartPage
;
265 DbgPrint((DPRINT_MEMORY
, "Marking the page lookup table pages as reserved StartPage: %d PageCount: %d\n", PageLookupTableStartPage
, PageLookupTablePageCount
));
266 MmMarkPagesInLookupTable(PageLookupTable
, PageLookupTableStartPage
, PageLookupTablePageCount
, LoaderFirmwareTemporary
);
269 VOID
MmMarkPagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
, TYPE_OF_MEMORY PageAllocated
)
271 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
274 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
276 if ((Index
<= (StartPage
+ 16)) || (Index
>= (StartPage
+PageCount
-16)))
278 DbgPrint((DPRINT_MEMORY
, "Index = %d StartPage = %d PageCount = %d\n", Index
, StartPage
, PageCount
));
280 RealPageLookupTable
[Index
].PageAllocated
= PageAllocated
;
281 RealPageLookupTable
[Index
].PageAllocationLength
= (PageAllocated
!= LoaderFree
) ? 1 : 0;
283 DbgPrint((DPRINT_MEMORY
, "MmMarkPagesInLookupTable() Done\n"));
286 VOID
MmAllocatePagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
)
288 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
291 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
293 RealPageLookupTable
[Index
].PageAllocated
= LoaderSystemCode
;
294 RealPageLookupTable
[Index
].PageAllocationLength
= (Index
== StartPage
) ? PageCount
: 0;
298 ULONG
MmCountFreePagesInLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
)
300 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
305 for (Index
=0; Index
<TotalPageCount
; Index
++)
307 if (RealPageLookupTable
[Index
].PageAllocated
== LoaderFree
)
313 return FreePageCount
;
316 ULONG
MmFindAvailablePages(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
, BOOLEAN FromEnd
)
318 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
319 ULONG AvailablePagesSoFar
;
322 if (LastFreePageHint
> TotalPageCount
)
324 LastFreePageHint
= TotalPageCount
;
327 AvailablePagesSoFar
= 0;
330 /* Allocate "high" (from end) pages */
331 for (Index
=LastFreePageHint
-1; Index
>0; Index
--)
333 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
335 AvailablePagesSoFar
= 0;
340 AvailablePagesSoFar
++;
343 if (AvailablePagesSoFar
>= PagesNeeded
)
351 DbgPrint((DPRINT_MEMORY
, "Alloc low memory, LastFreePageHint %d, TPC %d\n", LastFreePageHint
, TotalPageCount
));
352 /* Allocate "low" pages */
353 for (Index
=1; Index
< LastFreePageHint
; Index
++)
355 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
357 AvailablePagesSoFar
= 0;
362 AvailablePagesSoFar
++;
365 if (AvailablePagesSoFar
>= PagesNeeded
)
367 return Index
- AvailablePagesSoFar
+ 1;
375 ULONG
MmFindAvailablePagesBeforePage(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
, ULONG LastPage
)
377 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
378 ULONG AvailablePagesSoFar
;
381 if (LastPage
> TotalPageCount
)
383 return MmFindAvailablePages(PageLookupTable
, TotalPageCount
, PagesNeeded
, TRUE
);
386 AvailablePagesSoFar
= 0;
387 for (Index
=LastPage
-1; Index
>0; Index
--)
389 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)
391 AvailablePagesSoFar
= 0;
396 AvailablePagesSoFar
++;
399 if (AvailablePagesSoFar
>= PagesNeeded
)
408 VOID
MmFixupSystemMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG
* MapCount
)
413 // Loop through each entry in the array
414 for (Index
=0; Index
<*MapCount
; Index
++)
416 // If the entry type isn't usable then remove
417 // it from the memory map (this will help reduce
418 // the size of our lookup table)
419 if (BiosMemoryMap
[Index
].Type
!= BiosMemoryUsable
)
421 // Slide every entry after this down one
422 for (Index2
=Index
; Index2
<(*MapCount
- 1); Index2
++)
424 BiosMemoryMap
[Index2
] = BiosMemoryMap
[Index2
+ 1];
432 VOID
MmUpdateLastFreePageHint(PVOID PageLookupTable
, ULONG TotalPageCount
)
434 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
437 for (Index
=TotalPageCount
-1; Index
>0; Index
--)
439 if (RealPageLookupTable
[Index
].PageAllocated
== LoaderFree
)
441 LastFreePageHint
= Index
+ 1;
447 BOOLEAN
MmAreMemoryPagesAvailable(PVOID PageLookupTable
, ULONG TotalPageCount
, PVOID PageAddress
, ULONG PageCount
)
449 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
453 StartPage
= MmGetPageNumberFromAddress(PageAddress
);
455 // Make sure they aren't trying to go past the
456 // end of availabe memory
457 if ((StartPage
+ PageCount
) > TotalPageCount
)
462 for (Index
=StartPage
; Index
<(StartPage
+ PageCount
); Index
++)
464 // If this page is allocated then there obviously isn't
465 // memory availabe so return FALSE
466 if (RealPageLookupTable
[Index
].PageAllocated
!= LoaderFree
)