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.
35 } MEMORY_TYPE
, *PMEMORY_TYPE
;
37 ULONG MemoryTypeCount
= 5;
38 MEMORY_TYPE MemoryTypeArray
[] =
40 { 0, "Unknown Memory" },
41 { MEMTYPE_USABLE
, "Usable Memory" },
42 { MEMTYPE_RESERVED
, "Reserved Memory" },
43 { MEMTYPE_ACPI_RECLAIM
, "ACPI Reclaim Memory" },
44 { MEMTYPE_ACPI_NVS
, "ACPI NVS Memory" },
48 PVOID PageLookupTableAddress
= NULL
;
49 ULONG TotalPagesInLookupTable
= 0;
50 ULONG FreePagesInLookupTable
= 0;
51 ULONG LastFreePageHint
= 0;
53 BOOL
MmInitializeMemoryManager(VOID
)
55 BIOS_MEMORY_MAP BiosMemoryMap
[32];
56 ULONG BiosMemoryMapEntryCount
;
61 DbgPrint((DPRINT_MEMORY
, "Initializing Memory Manager.\n"));
63 RtlZeroMemory(BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
65 BiosMemoryMapEntryCount
= MachGetMemoryMap(BiosMemoryMap
, sizeof(BiosMemoryMap
) / sizeof(BIOS_MEMORY_MAP
));
68 // Dump the system memory map
69 if (BiosMemoryMapEntryCount
!= 0)
71 DbgPrint((DPRINT_MEMORY
, "System Memory Map (Base Address, Length, Type):\n"));
72 for (Index
=0; Index
<BiosMemoryMapEntryCount
; Index
++)
74 DbgPrint((DPRINT_MEMORY
, "%x%x\t %x%x\t %s\n", BiosMemoryMap
[Index
].BaseAddress
, BiosMemoryMap
[Index
].Length
, MmGetSystemMemoryMapTypeString(BiosMemoryMap
[Index
].Type
)));
79 // If we got the system memory map then fixup invalid entries
80 if (BiosMemoryMapEntryCount
!= 0)
82 MmFixupSystemMemoryMap(BiosMemoryMap
, &BiosMemoryMapEntryCount
);
85 TotalPagesInLookupTable
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, BiosMemoryMapEntryCount
);
86 PageLookupTableAddress
= MmFindLocationForPageLookupTable(BiosMemoryMap
, BiosMemoryMapEntryCount
);
87 LastFreePageHint
= TotalPagesInLookupTable
;
89 if (PageLookupTableAddress
== 0)
91 // If we get here then we probably couldn't
92 // find a contiguous chunk of memory big
93 // enough to hold the page lookup table
94 printf("Error initializing memory manager!\n");
98 MmInitPageLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
, BiosMemoryMap
, BiosMemoryMapEntryCount
);
99 MmUpdateLastFreePageHint(PageLookupTableAddress
, TotalPagesInLookupTable
);
101 FreePagesInLookupTable
= MmCountFreePagesInLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
);
103 DbgPrint((DPRINT_MEMORY
, "Memory Manager initialized. %d pages available.\n", FreePagesInLookupTable
));
108 PUCHAR
MmGetSystemMemoryMapTypeString(ULONG Type
)
112 for (Index
=1; Index
<MemoryTypeCount
; Index
++)
114 if (MemoryTypeArray
[Index
].Type
== Type
)
116 return MemoryTypeArray
[Index
].TypeString
;
120 return MemoryTypeArray
[0].TypeString
;
124 ULONG
MmGetPageNumberFromAddress(PVOID Address
)
126 return ((ULONG
)Address
) / MM_PAGE_SIZE
;
129 PVOID
MmGetEndAddressOfAnyMemory(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
131 ULONGLONG MaxStartAddressSoFar
;
132 ULONGLONG EndAddressOfMemory
;
135 MaxStartAddressSoFar
= 0;
136 EndAddressOfMemory
= 0;
137 for (Index
=0; Index
<MapCount
; Index
++)
139 if (MaxStartAddressSoFar
<= BiosMemoryMap
[Index
].BaseAddress
)
141 MaxStartAddressSoFar
= BiosMemoryMap
[Index
].BaseAddress
;
142 EndAddressOfMemory
= (MaxStartAddressSoFar
+ BiosMemoryMap
[Index
].Length
);
143 if (EndAddressOfMemory
> 0xFFFFFFFF)
145 EndAddressOfMemory
= 0xFFFFFFFF;
150 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returning 0x%x\n", (ULONG
)EndAddressOfMemory
));
152 return (PVOID
)(ULONG
)EndAddressOfMemory
;
155 ULONG
MmGetAddressablePageCountIncludingHoles(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
158 ULONGLONG EndAddress
;
160 EndAddress
= (ULONGLONG
)(ULONG
)MmGetEndAddressOfAnyMemory(BiosMemoryMap
, MapCount
);
162 // Since MmGetEndAddressOfAnyMemory() won't
163 // return addresses higher than 0xFFFFFFFF
164 // then we need to adjust the end address
165 // to 0x100000000 so we don't get an
167 if (EndAddress
>= 0xFFFFFFFF)
169 EndAddress
= 0x100000000LL
;
171 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returned 0xFFFFFFFF, correcting to be 0x100000000.\n"));
174 PageCount
= (EndAddress
/ MM_PAGE_SIZE
);
176 DbgPrint((DPRINT_MEMORY
, "MmGetAddressablePageCountIncludingHoles() returning %d\n", PageCount
));
181 PVOID
MmFindLocationForPageLookupTable(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
183 ULONG TotalPageCount
;
184 ULONG PageLookupTableSize
;
185 PVOID PageLookupTableMemAddress
;
187 BIOS_MEMORY_MAP TempBiosMemoryMap
[32];
189 TotalPageCount
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, MapCount
);
190 PageLookupTableSize
= TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
);
191 PageLookupTableMemAddress
= 0;
193 RtlCopyMemory(TempBiosMemoryMap
, BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
194 MmSortBiosMemoryMap(TempBiosMemoryMap
, MapCount
);
196 for (Index
=(MapCount
-1); Index
>=0; Index
--)
198 // If this is usable memory with a big enough length
199 // then we'll put our page lookup table here
200 if (TempBiosMemoryMap
[Index
].Type
== MEMTYPE_USABLE
&& TempBiosMemoryMap
[Index
].Length
>= PageLookupTableSize
)
202 PageLookupTableMemAddress
= (PVOID
)(ULONG
)(TempBiosMemoryMap
[Index
].BaseAddress
+ (TempBiosMemoryMap
[Index
].Length
- PageLookupTableSize
));
207 DbgPrint((DPRINT_MEMORY
, "MmFindLocationForPageLookupTable() returning 0x%x\n", PageLookupTableMemAddress
));
209 return PageLookupTableMemAddress
;
212 VOID
MmSortBiosMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
216 BIOS_MEMORY_MAP TempMapItem
;
218 // Loop once for each entry in the memory map minus one
219 // On each loop iteration go through and sort the memory map
220 for (LoopCount
=0; LoopCount
<(MapCount
-1); LoopCount
++)
222 for (Index
=0; Index
<(MapCount
-1); Index
++)
224 if (BiosMemoryMap
[Index
].BaseAddress
> BiosMemoryMap
[Index
+1].BaseAddress
)
226 TempMapItem
= BiosMemoryMap
[Index
];
227 BiosMemoryMap
[Index
] = BiosMemoryMap
[Index
+1];
228 BiosMemoryMap
[Index
+1] = TempMapItem
;
234 VOID
MmInitPageLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
, PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG MapCount
)
236 ULONG MemoryMapStartPage
;
237 ULONG MemoryMapEndPage
;
238 ULONG MemoryMapPageCount
;
239 ULONG MemoryMapPageAllocated
;
240 ULONG PageLookupTableStartPage
;
241 ULONG PageLookupTablePageCount
;
244 DbgPrint((DPRINT_MEMORY
, "MmInitPageLookupTable()\n"));
246 // Mark every page as allocated initially
247 // We will go through and mark pages again according to the memory map
248 // But this will mark any holes not described in the map as allocated
249 MmMarkPagesInLookupTable(PageLookupTable
, 0, TotalPageCount
, 1);
251 for (Index
=0; Index
<MapCount
; Index
++)
253 MemoryMapStartPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG
)BiosMemoryMap
[Index
].BaseAddress
);
254 MemoryMapEndPage
= MmGetPageNumberFromAddress((PVOID
)(ULONG
)(BiosMemoryMap
[Index
].BaseAddress
+ BiosMemoryMap
[Index
].Length
- 1));
255 MemoryMapPageCount
= (MemoryMapEndPage
- MemoryMapStartPage
) + 1;
256 MemoryMapPageAllocated
= (BiosMemoryMap
[Index
].Type
== MEMTYPE_USABLE
) ? 0 : BiosMemoryMap
[Index
].Type
;
257 DbgPrint((DPRINT_MEMORY
, "Marking pages as type %d: StartPage: %d PageCount: %d\n", MemoryMapPageAllocated
, MemoryMapStartPage
, MemoryMapPageCount
));
258 MmMarkPagesInLookupTable(PageLookupTable
, MemoryMapStartPage
, MemoryMapPageCount
, MemoryMapPageAllocated
);
261 // Mark the low memory region below 1MB as reserved (256 pages in region)
262 DbgPrint((DPRINT_MEMORY
, "Marking the low 1MB region as reserved.\n"));
263 MmMarkPagesInLookupTable(PageLookupTable
, 0, 256, MEMTYPE_RESERVED
);
265 // Mark the pages that the lookup tabel occupies as reserved
266 PageLookupTableStartPage
= MmGetPageNumberFromAddress(PageLookupTable
);
267 PageLookupTablePageCount
= MmGetPageNumberFromAddress((PVOID
)((ULONG_PTR
)PageLookupTable
+ ROUND_UP(TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
), MM_PAGE_SIZE
))) - PageLookupTableStartPage
;
268 DbgPrint((DPRINT_MEMORY
, "Marking the page lookup table pages as reserved StartPage: %d PageCount: %d\n", PageLookupTableStartPage
, PageLookupTablePageCount
));
269 MmMarkPagesInLookupTable(PageLookupTable
, PageLookupTableStartPage
, PageLookupTablePageCount
, MEMTYPE_RESERVED
);
272 VOID
MmMarkPagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
, ULONG PageAllocated
)
274 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
277 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
279 if ((Index
<= (StartPage
+ 16)) || (Index
>= (StartPage
+PageCount
-16)))
281 DbgPrint((DPRINT_MEMORY
, "Index = %d StartPage = %d PageCount = %d\n", Index
, StartPage
, PageCount
));
283 RealPageLookupTable
[Index
].PageAllocated
= PageAllocated
;
284 RealPageLookupTable
[Index
].PageAllocationLength
= PageAllocated
? 1 : 0;
286 DbgPrint((DPRINT_MEMORY
, "MmMarkPagesInLookupTable() Done\n"));
289 VOID
MmAllocatePagesInLookupTable(PVOID PageLookupTable
, ULONG StartPage
, ULONG PageCount
)
291 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
294 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
296 RealPageLookupTable
[Index
].PageAllocated
= 1;
297 RealPageLookupTable
[Index
].PageAllocationLength
= (Index
== StartPage
) ? PageCount
: 0;
301 ULONG
MmCountFreePagesInLookupTable(PVOID PageLookupTable
, ULONG TotalPageCount
)
303 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
308 for (Index
=0; Index
<TotalPageCount
; Index
++)
310 if (RealPageLookupTable
[Index
].PageAllocated
== 0)
316 return FreePageCount
;
319 ULONG
MmFindAvailablePagesFromEnd(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
)
321 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
322 ULONG AvailablePagesSoFar
;
325 if (LastFreePageHint
> TotalPageCount
)
327 LastFreePageHint
= TotalPageCount
;
330 AvailablePagesSoFar
= 0;
331 for (Index
=LastFreePageHint
-1; Index
>0; Index
--)
333 if (RealPageLookupTable
[Index
].PageAllocated
!= 0)
335 AvailablePagesSoFar
= 0;
340 AvailablePagesSoFar
++;
343 if (AvailablePagesSoFar
>= PagesNeeded
)
352 ULONG
MmFindAvailablePagesBeforePage(PVOID PageLookupTable
, ULONG TotalPageCount
, ULONG PagesNeeded
, ULONG LastPage
)
354 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
355 ULONG AvailablePagesSoFar
;
358 if (LastPage
> TotalPageCount
)
360 return MmFindAvailablePagesFromEnd(PageLookupTable
, TotalPageCount
, PagesNeeded
);
363 AvailablePagesSoFar
= 0;
364 for (Index
=LastPage
-1; Index
>0; Index
--)
366 if (RealPageLookupTable
[Index
].PageAllocated
!= 0)
368 AvailablePagesSoFar
= 0;
373 AvailablePagesSoFar
++;
376 if (AvailablePagesSoFar
>= PagesNeeded
)
385 VOID
MmFixupSystemMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, ULONG
* MapCount
)
390 // Loop through each entry in the array
391 for (Index
=0; Index
<*MapCount
; Index
++)
393 // If the entry type isn't usable then remove
394 // it from the memory map (this will help reduce
395 // the size of our lookup table)
396 if (BiosMemoryMap
[Index
].Type
!= MEMTYPE_USABLE
)
398 // Slide every entry after this down one
399 for (Index2
=Index
; Index2
<(*MapCount
- 1); Index2
++)
401 BiosMemoryMap
[Index2
] = BiosMemoryMap
[Index2
+ 1];
409 VOID
MmUpdateLastFreePageHint(PVOID PageLookupTable
, ULONG TotalPageCount
)
411 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
414 for (Index
=TotalPageCount
-1; Index
>0; Index
--)
416 if (RealPageLookupTable
[Index
].PageAllocated
== 0)
418 LastFreePageHint
= Index
+ 1;
424 BOOL
MmAreMemoryPagesAvailable(PVOID PageLookupTable
, ULONG TotalPageCount
, PVOID PageAddress
, ULONG PageCount
)
426 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
430 StartPage
= MmGetPageNumberFromAddress(PageAddress
);
432 // Make sure they aren't trying to go past the
433 // end of availabe memory
434 if ((StartPage
+ PageCount
) > TotalPageCount
)
439 for (Index
=StartPage
; Index
<(StartPage
+ PageCount
); Index
++)
441 // If this page is allocated then there obviously isn't
442 // memory availabe so return FALSE
443 if (RealPageLookupTable
[Index
].PageAllocated
!= 0)