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.
34 } MEMORY_TYPE
, *PMEMORY_TYPE
;
36 U32 MemoryTypeCount
= 5;
37 MEMORY_TYPE MemoryTypeArray
[] =
39 { 0, "Unknown Memory" },
40 { MEMTYPE_USABLE
, "Usable Memory" },
41 { MEMTYPE_RESERVED
, "Reserved Memory" },
42 { MEMTYPE_ACPI_RECLAIM
, "ACPI Reclaim Memory" },
43 { MEMTYPE_ACPI_NVS
, "ACPI NVS Memory" },
47 PVOID PageLookupTableAddress
= NULL
;
48 U32 TotalPagesInLookupTable
= 0;
49 U32 FreePagesInLookupTable
= 0;
50 U32 LastFreePageHint
= 0;
52 BOOL
MmInitializeMemoryManager(VOID
)
54 BIOS_MEMORY_MAP BiosMemoryMap
[32];
55 U32 BiosMemoryMapEntryCount
;
56 U32 ExtendedMemorySize
;
57 U32 ConventionalMemorySize
;
62 DbgPrint((DPRINT_MEMORY
, "Initializing Memory Manager.\n"));
64 RtlZeroMemory(BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
66 BiosMemoryMapEntryCount
= GetBiosMemoryMap(BiosMemoryMap
, 32);
67 ExtendedMemorySize
= GetExtendedMemorySize();
68 ConventionalMemorySize
= GetConventionalMemorySize();
71 // Dump the system memory map
72 if (BiosMemoryMapEntryCount
!= 0)
74 DbgPrint((DPRINT_MEMORY
, "System Memory Map (Base Address, Length, Type):\n"));
75 for (Index
=0; Index
<BiosMemoryMapEntryCount
; Index
++)
77 DbgPrint((DPRINT_MEMORY
, "%x%x\t %x%x\t %s\n", BiosMemoryMap
[Index
].BaseAddress
, BiosMemoryMap
[Index
].Length
, MmGetSystemMemoryMapTypeString(BiosMemoryMap
[Index
].Type
)));
82 DbgPrint((DPRINT_MEMORY
, "GetBiosMemoryMap() not supported.\n"));
85 DbgPrint((DPRINT_MEMORY
, "Extended memory size: %d KB\n", ExtendedMemorySize
));
86 DbgPrint((DPRINT_MEMORY
, "Conventional memory size: %d KB\n", ConventionalMemorySize
));
89 // If we got the system memory map then fixup invalid entries
90 if (BiosMemoryMapEntryCount
!= 0)
92 MmFixupSystemMemoryMap(BiosMemoryMap
, &BiosMemoryMapEntryCount
);
95 // Since I don't feel like writing two sets of routines
96 // one to handle the BiosMemoryMap structure and another
97 // to handle just a flat extended memory size I'm going
98 // to create a 'fake' memory map entry out of the
99 // extended memory size if GetBiosMemoryMap() fails.
100 //if (BiosMemoryMapEntryCount == 0)
102 BiosMemoryMap
[0].BaseAddress
= 0x100000; // Start at 1MB
103 BiosMemoryMap
[0].Length
= ExtendedMemorySize
* 1024;
104 BiosMemoryMap
[0].Type
= MEMTYPE_USABLE
;
105 BiosMemoryMapEntryCount
= 1;
108 TotalPagesInLookupTable
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, BiosMemoryMapEntryCount
);
109 PageLookupTableAddress
= MmFindLocationForPageLookupTable(BiosMemoryMap
, BiosMemoryMapEntryCount
);
110 LastFreePageHint
= TotalPagesInLookupTable
;
112 if (PageLookupTableAddress
== 0)
114 // If we get here then we probably couldn't
115 // find a contiguous chunk of memory big
116 // enough to hold the page lookup table
117 printf("Error initializing memory manager!\n");
121 MmInitPageLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
, BiosMemoryMap
, BiosMemoryMapEntryCount
);
122 MmUpdateLastFreePageHint(PageLookupTableAddress
, TotalPagesInLookupTable
);
124 FreePagesInLookupTable
= MmCountFreePagesInLookupTable(PageLookupTableAddress
, TotalPagesInLookupTable
);
126 DbgPrint((DPRINT_MEMORY
, "Memory Manager initialized. %d pages available.\n", FreePagesInLookupTable
));
131 PUCHAR
MmGetSystemMemoryMapTypeString(U32 Type
)
135 for (Index
=1; Index
<MemoryTypeCount
; Index
++)
137 if (MemoryTypeArray
[Index
].Type
== Type
)
139 return MemoryTypeArray
[Index
].TypeString
;
143 return MemoryTypeArray
[0].TypeString
;
147 U32
MmGetPageNumberFromAddress(PVOID Address
)
149 return ((U32
)Address
) / MM_PAGE_SIZE
;
152 PVOID
MmGetEndAddressOfAnyMemory(PBIOS_MEMORY_MAP BiosMemoryMap
, U32 MapCount
)
154 U64 MaxStartAddressSoFar
;
155 U64 EndAddressOfMemory
;
158 MaxStartAddressSoFar
= 0;
159 EndAddressOfMemory
= 0;
160 for (Index
=0; Index
<MapCount
; Index
++)
162 if (MaxStartAddressSoFar
< BiosMemoryMap
[Index
].BaseAddress
)
164 MaxStartAddressSoFar
= BiosMemoryMap
[Index
].BaseAddress
;
165 EndAddressOfMemory
= (MaxStartAddressSoFar
+ BiosMemoryMap
[Index
].Length
);
166 if (EndAddressOfMemory
> 0xFFFFFFFF)
168 EndAddressOfMemory
= 0xFFFFFFFF;
173 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returning 0x%x\n", (U32
)EndAddressOfMemory
));
175 return (PVOID
)(U32
)EndAddressOfMemory
;
178 U32
MmGetAddressablePageCountIncludingHoles(PBIOS_MEMORY_MAP BiosMemoryMap
, U32 MapCount
)
183 EndAddress
= (U64
)(U32
)MmGetEndAddressOfAnyMemory(BiosMemoryMap
, MapCount
);
185 // Since MmGetEndAddressOfAnyMemory() won't
186 // return addresses higher than 0xFFFFFFFF
187 // then we need to adjust the end address
188 // to 0x100000000 so we don't get an
190 if (EndAddress
>= 0xFFFFFFFF)
192 EndAddress
= 0x100000000LL
;
194 DbgPrint((DPRINT_MEMORY
, "MmGetEndAddressOfAnyMemory() returned 0xFFFFFFFF, correcting to be 0x100000000.\n"));
197 PageCount
= (EndAddress
/ MM_PAGE_SIZE
);
199 DbgPrint((DPRINT_MEMORY
, "MmGetAddressablePageCountIncludingHoles() returning %d\n", PageCount
));
204 PVOID
MmFindLocationForPageLookupTable(PBIOS_MEMORY_MAP BiosMemoryMap
, U32 MapCount
)
207 U32 PageLookupTableSize
;
208 PVOID PageLookupTableMemAddress
;
210 BIOS_MEMORY_MAP TempBiosMemoryMap
[32];
212 TotalPageCount
= MmGetAddressablePageCountIncludingHoles(BiosMemoryMap
, MapCount
);
213 PageLookupTableSize
= TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
);
214 PageLookupTableMemAddress
= 0;
216 RtlCopyMemory(TempBiosMemoryMap
, BiosMemoryMap
, sizeof(BIOS_MEMORY_MAP
) * 32);
217 MmSortBiosMemoryMap(TempBiosMemoryMap
, MapCount
);
219 for (Index
=(MapCount
-1); Index
>=0; Index
--)
221 // If this is usable memory with a big enough length
222 // then we'll put our page lookup table here
223 if (TempBiosMemoryMap
[Index
].Type
== MEMTYPE_USABLE
&& TempBiosMemoryMap
[Index
].Length
>= PageLookupTableSize
)
225 PageLookupTableMemAddress
= (PVOID
)(U32
)(TempBiosMemoryMap
[Index
].BaseAddress
+ (TempBiosMemoryMap
[Index
].Length
- PageLookupTableSize
));
230 DbgPrint((DPRINT_MEMORY
, "MmFindLocationForPageLookupTable() returning 0x%x\n", PageLookupTableMemAddress
));
232 return PageLookupTableMemAddress
;
235 VOID
MmSortBiosMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, U32 MapCount
)
239 BIOS_MEMORY_MAP TempMapItem
;
241 // Loop once for each entry in the memory map minus one
242 // On each loop iteration go through and sort the memory map
243 for (LoopCount
=0; LoopCount
<(MapCount
-1); LoopCount
++)
245 for (Index
=0; Index
<(MapCount
-1); Index
++)
247 if (BiosMemoryMap
[Index
].BaseAddress
> BiosMemoryMap
[Index
+1].BaseAddress
)
249 TempMapItem
= BiosMemoryMap
[Index
];
250 BiosMemoryMap
[Index
] = BiosMemoryMap
[Index
+1];
251 BiosMemoryMap
[Index
+1] = TempMapItem
;
257 VOID
MmInitPageLookupTable(PVOID PageLookupTable
, U32 TotalPageCount
, PBIOS_MEMORY_MAP BiosMemoryMap
, U32 MapCount
)
259 U32 MemoryMapStartPage
;
260 U32 MemoryMapEndPage
;
261 U32 MemoryMapPageCount
;
262 U32 MemoryMapPageAllocated
;
263 U32 PageLookupTableStartPage
;
264 U32 PageLookupTablePageCount
;
267 DbgPrint((DPRINT_MEMORY
, "MmInitPageLookupTable()\n"));
269 // Mark every page as allocated initially
270 // We will go through and mark pages again according to the memory map
271 // But this will mark any holes not described in the map as allocated
272 MmMarkPagesInLookupTable(PageLookupTable
, 0, TotalPageCount
, 1);
274 for (Index
=0; Index
<MapCount
; Index
++)
276 MemoryMapStartPage
= MmGetPageNumberFromAddress((PVOID
)(U32
)BiosMemoryMap
[Index
].BaseAddress
);
277 MemoryMapEndPage
= MmGetPageNumberFromAddress((PVOID
)(U32
)(BiosMemoryMap
[Index
].BaseAddress
+ BiosMemoryMap
[Index
].Length
- 1));
278 MemoryMapPageCount
= (MemoryMapEndPage
- MemoryMapStartPage
) + 1;
279 MemoryMapPageAllocated
= (BiosMemoryMap
[Index
].Type
== MEMTYPE_USABLE
) ? 0 : BiosMemoryMap
[Index
].Type
;
280 DbgPrint((DPRINT_MEMORY
, "Marking pages as type %d: StartPage: %d PageCount: %d\n", MemoryMapPageAllocated
, MemoryMapStartPage
, MemoryMapPageCount
));
281 MmMarkPagesInLookupTable(PageLookupTable
, MemoryMapStartPage
, MemoryMapPageCount
, MemoryMapPageAllocated
);
284 // Mark the low memory region below 1MB as reserved (256 pages in region)
285 DbgPrint((DPRINT_MEMORY
, "Marking the low 1MB region as reserved.\n"));
286 MmMarkPagesInLookupTable(PageLookupTable
, 0, 256, MEMTYPE_RESERVED
);
288 // Mark the pages that the lookup tabel occupies as reserved
289 PageLookupTableStartPage
= MmGetPageNumberFromAddress(PageLookupTable
);
290 PageLookupTablePageCount
= MmGetPageNumberFromAddress(PageLookupTable
+ ROUND_UP(TotalPageCount
* sizeof(PAGE_LOOKUP_TABLE_ITEM
), MM_PAGE_SIZE
)) - PageLookupTableStartPage
;
291 DbgPrint((DPRINT_MEMORY
, "Marking the page lookup table pages as reserved StartPage: %d PageCount: %d\n", PageLookupTableStartPage
, PageLookupTablePageCount
));
292 MmMarkPagesInLookupTable(PageLookupTable
, PageLookupTableStartPage
, PageLookupTablePageCount
, MEMTYPE_RESERVED
);
295 VOID
MmMarkPagesInLookupTable(PVOID PageLookupTable
, U32 StartPage
, U32 PageCount
, U32 PageAllocated
)
297 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
300 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
302 if ((Index
<= (StartPage
+ 16)) || (Index
>= (StartPage
+PageCount
-16)))
304 DbgPrint((DPRINT_MEMORY
, "Index = %d StartPage = %d PageCount = %d\n", Index
, StartPage
, PageCount
));
306 RealPageLookupTable
[Index
].PageAllocated
= PageAllocated
;
307 RealPageLookupTable
[Index
].PageAllocationLength
= PageAllocated
? 1 : 0;
309 DbgPrint((DPRINT_MEMORY
, "MmMarkPagesInLookupTable() Done\n"));
312 VOID
MmAllocatePagesInLookupTable(PVOID PageLookupTable
, U32 StartPage
, U32 PageCount
)
314 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
317 for (Index
=StartPage
; Index
<(StartPage
+PageCount
); Index
++)
319 RealPageLookupTable
[Index
].PageAllocated
= 1;
320 RealPageLookupTable
[Index
].PageAllocationLength
= (Index
== StartPage
) ? PageCount
: 0;
324 U32
MmCountFreePagesInLookupTable(PVOID PageLookupTable
, U32 TotalPageCount
)
326 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
331 for (Index
=0; Index
<TotalPageCount
; Index
++)
333 if (RealPageLookupTable
[Index
].PageAllocated
== 0)
339 return FreePageCount
;
342 U32
MmFindAvailablePagesFromEnd(PVOID PageLookupTable
, U32 TotalPageCount
, U32 PagesNeeded
)
344 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
345 U32 AvailablePagesSoFar
;
348 if (LastFreePageHint
> TotalPageCount
)
350 LastFreePageHint
= TotalPageCount
;
353 AvailablePagesSoFar
= 0;
354 for (Index
=LastFreePageHint
-1; Index
>0; Index
--)
356 if (RealPageLookupTable
[Index
].PageAllocated
!= 0)
358 AvailablePagesSoFar
= 0;
363 AvailablePagesSoFar
++;
366 if (AvailablePagesSoFar
>= PagesNeeded
)
375 U32
MmFindAvailablePagesBeforePage(PVOID PageLookupTable
, U32 TotalPageCount
, U32 PagesNeeded
, U32 LastPage
)
377 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable
= (PPAGE_LOOKUP_TABLE_ITEM
)PageLookupTable
;
378 U32 AvailablePagesSoFar
;
381 if (LastPage
> TotalPageCount
)
383 return MmFindAvailablePagesFromEnd(PageLookupTable
, TotalPageCount
, PagesNeeded
);
386 AvailablePagesSoFar
= 0;
387 for (Index
=LastPage
-1; Index
>0; Index
--)
389 if (RealPageLookupTable
[Index
].PageAllocated
!= 0)
391 AvailablePagesSoFar
= 0;
396 AvailablePagesSoFar
++;
399 if (AvailablePagesSoFar
>= PagesNeeded
)
408 VOID
MmFixupSystemMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap
, U32
* 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
!= MEMTYPE_USABLE
)
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
, U32 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
== 0)
441 LastFreePageHint
= Index
+ 1;
447 BOOL
MmAreMemoryPagesAvailable(PVOID PageLookupTable
, U32 TotalPageCount
, PVOID PageAddress
, U32 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
!= 0)