sync with trunk head (34904)
[reactos.git] / reactos / boot / freeldr / freeldr / mm / meminit.c
1 /*
2 * FreeLoader
3 * Copyright (C) 2006-2008 Aleksey Bragin <aleksey@reactos.org>
4 *
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.
9 *
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.
14 *
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.
18 */
19
20 #include <freeldr.h>
21 #include <debug.h>
22
23 #ifdef DBG
24 typedef struct
25 {
26 ULONG Type;
27 UCHAR TypeString[20];
28 } FREELDR_MEMORY_TYPE, *PFREELDR_MEMORY_TYPE;
29
30 ULONG MemoryTypeCount = 5;
31 FREELDR_MEMORY_TYPE MemoryTypeArray[] =
32 {
33 { 0, "Unknown Memory" },
34 { BiosMemoryUsable, "Usable Memory" },
35 { BiosMemoryReserved, "Reserved Memory" },
36 { BiosMemoryAcpiReclaim, "ACPI Reclaim Memory" },
37 { BiosMemoryAcpiNvs, "ACPI NVS Memory" },
38 };
39 #endif
40
41 PVOID PageLookupTableAddress = NULL;
42 ULONG TotalPagesInLookupTable = 0;
43 ULONG FreePagesInLookupTable = 0;
44 ULONG LastFreePageHint = 0;
45
46 extern ULONG_PTR MmHeapPointer;
47 extern ULONG_PTR MmHeapStart;
48
49 BOOLEAN MmInitializeMemoryManager(VOID)
50 {
51 BIOS_MEMORY_MAP BiosMemoryMap[32];
52 ULONG BiosMemoryMapEntryCount;
53 #ifdef DBG
54 ULONG Index;
55 #endif
56
57 DbgPrint((DPRINT_MEMORY, "Initializing Memory Manager.\n"));
58
59 RtlZeroMemory(BiosMemoryMap, sizeof(BIOS_MEMORY_MAP) * 32);
60
61 BiosMemoryMapEntryCount = MachGetMemoryMap(BiosMemoryMap, sizeof(BiosMemoryMap) / sizeof(BIOS_MEMORY_MAP));
62
63 #ifdef DBG
64 // Dump the system memory map
65 if (BiosMemoryMapEntryCount != 0)
66 {
67 DbgPrint((DPRINT_MEMORY, "System Memory Map (Base Address, Length, Type):\n"));
68 for (Index=0; Index<BiosMemoryMapEntryCount; Index++)
69 {
70 DbgPrint((DPRINT_MEMORY, "%x%x\t %x%x\t %s\n", BiosMemoryMap[Index].BaseAddress, BiosMemoryMap[Index].Length, MmGetSystemMemoryMapTypeString(BiosMemoryMap[Index].Type)));
71 }
72 }
73 #endif
74
75 // If we got the system memory map then fixup invalid entries
76 if (BiosMemoryMapEntryCount != 0)
77 {
78 MmFixupSystemMemoryMap(BiosMemoryMap, &BiosMemoryMapEntryCount);
79 }
80
81 // Find address for the page lookup table
82 TotalPagesInLookupTable = MmGetAddressablePageCountIncludingHoles(BiosMemoryMap, BiosMemoryMapEntryCount);
83 PageLookupTableAddress = MmFindLocationForPageLookupTable(BiosMemoryMap, BiosMemoryMapEntryCount);
84 LastFreePageHint = TotalPagesInLookupTable;
85
86 if (PageLookupTableAddress == 0)
87 {
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");
92 return FALSE;
93 }
94
95 // Initialize the page lookup table
96 MmInitPageLookupTable(PageLookupTableAddress, TotalPagesInLookupTable, BiosMemoryMap, BiosMemoryMapEntryCount);
97 MmUpdateLastFreePageHint(PageLookupTableAddress, TotalPagesInLookupTable);
98
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
111 #endif
112 #elif __arm__
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)
116 #endif
117
118 FreePagesInLookupTable = MmCountFreePagesInLookupTable(PageLookupTableAddress, TotalPagesInLookupTable);
119
120 MmInitializeHeap(PageLookupTableAddress);
121
122 DbgPrint((DPRINT_MEMORY, "Memory Manager initialized. %d pages available.\n", FreePagesInLookupTable));
123 return TRUE;
124 }
125
126 VOID MmInitializeHeap(PVOID PageLookupTable)
127 {
128 ULONG PagesNeeded;
129 ULONG HeapStart;
130
131 // HACK: Make it so it doesn't overlap kernel space
132 MmMarkPagesInLookupTable(PageLookupTableAddress, 0x100, 0xFF, LoaderSystemCode);
133
134 // Find contigious memory block for HEAP:STACK
135 PagesNeeded = HEAP_PAGES + STACK_PAGES;
136 HeapStart = MmFindAvailablePages(PageLookupTable, TotalPagesInLookupTable, PagesNeeded, FALSE);
137
138 // Unapply the hack
139 MmMarkPagesInLookupTable(PageLookupTableAddress, 0x100, 0xFF, LoaderFree);
140
141 if (HeapStart == 0)
142 {
143 UiMessageBox("Critical error: Can't allocate heap!");
144 return;
145 }
146
147 // Initialize BGET
148 bpool(HeapStart << MM_PAGE_SHIFT, PagesNeeded << MM_PAGE_SHIFT);
149
150 // Mark those pages as used
151 MmMarkPagesInLookupTable(PageLookupTableAddress, HeapStart, PagesNeeded, LoaderOsloaderHeap);
152
153 DbgPrint((DPRINT_MEMORY, "Heap initialized, base 0x%08x, pages %d\n", (HeapStart << MM_PAGE_SHIFT), PagesNeeded));
154 }
155
156 #ifdef DBG
157 PUCHAR MmGetSystemMemoryMapTypeString(ULONG Type)
158 {
159 ULONG Index;
160
161 for (Index=1; Index<MemoryTypeCount; Index++)
162 {
163 if (MemoryTypeArray[Index].Type == Type)
164 {
165 return MemoryTypeArray[Index].TypeString;
166 }
167 }
168
169 return MemoryTypeArray[0].TypeString;
170 }
171 #endif
172
173 ULONG MmGetPageNumberFromAddress(PVOID Address)
174 {
175 return ((ULONG_PTR)Address) / MM_PAGE_SIZE;
176 }
177
178 PVOID MmGetEndAddressOfAnyMemory(PBIOS_MEMORY_MAP BiosMemoryMap, ULONG MapCount)
179 {
180 ULONGLONG MaxStartAddressSoFar;
181 ULONGLONG EndAddressOfMemory;
182 ULONG Index;
183
184 MaxStartAddressSoFar = 0;
185 EndAddressOfMemory = 0;
186 for (Index=0; Index<MapCount; Index++)
187 {
188 if (MaxStartAddressSoFar <= BiosMemoryMap[Index].BaseAddress)
189 {
190 MaxStartAddressSoFar = BiosMemoryMap[Index].BaseAddress;
191 EndAddressOfMemory = (MaxStartAddressSoFar + BiosMemoryMap[Index].Length);
192 if (EndAddressOfMemory > 0xFFFFFFFF)
193 {
194 EndAddressOfMemory = 0xFFFFFFFF;
195 }
196 }
197 }
198
199 DbgPrint((DPRINT_MEMORY, "MmGetEndAddressOfAnyMemory() returning 0x%x\n", (ULONG)EndAddressOfMemory));
200
201 return (PVOID)(ULONG_PTR)EndAddressOfMemory;
202 }
203
204 ULONG MmGetAddressablePageCountIncludingHoles(PBIOS_MEMORY_MAP BiosMemoryMap, ULONG MapCount)
205 {
206 ULONG PageCount;
207 ULONGLONG EndAddress;
208
209 EndAddress = (ULONGLONG)(ULONG_PTR)MmGetEndAddressOfAnyMemory(BiosMemoryMap, MapCount);
210
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
215 // off-by-one error
216 if (EndAddress >= 0xFFFFFFFF)
217 {
218 EndAddress = 0x100000000LL;
219
220 DbgPrint((DPRINT_MEMORY, "MmGetEndAddressOfAnyMemory() returned 0xFFFFFFFF, correcting to be 0x100000000.\n"));
221 }
222
223 PageCount = (EndAddress / MM_PAGE_SIZE);
224
225 DbgPrint((DPRINT_MEMORY, "MmGetAddressablePageCountIncludingHoles() returning %d\n", PageCount));
226
227 return PageCount;
228 }
229
230 PVOID MmFindLocationForPageLookupTable(PBIOS_MEMORY_MAP BiosMemoryMap, ULONG MapCount)
231 {
232 ULONG TotalPageCount;
233 ULONG PageLookupTableSize;
234 PVOID PageLookupTableMemAddress;
235 int Index;
236 BIOS_MEMORY_MAP TempBiosMemoryMap[32];
237
238 TotalPageCount = MmGetAddressablePageCountIncludingHoles(BiosMemoryMap, MapCount);
239 PageLookupTableSize = TotalPageCount * sizeof(PAGE_LOOKUP_TABLE_ITEM);
240 PageLookupTableMemAddress = 0;
241
242 RtlCopyMemory(TempBiosMemoryMap, BiosMemoryMap, sizeof(BIOS_MEMORY_MAP) * 32);
243 MmSortBiosMemoryMap(TempBiosMemoryMap, MapCount);
244
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--)
248 {
249 // If this is usable memory with a big enough length
250 // then we'll put our page lookup table here
251
252 // skip if this is not usable region
253 if (TempBiosMemoryMap[Index].Type != BiosMemoryUsable)
254 continue;
255
256 if (TempBiosMemoryMap[Index].Length >= PageLookupTableSize)
257 {
258 PageLookupTableMemAddress = (PVOID)(ULONG_PTR)
259 (TempBiosMemoryMap[Index].BaseAddress + (TempBiosMemoryMap[Index].Length - PageLookupTableSize));
260 break;
261 }
262 }
263
264 DbgPrint((DPRINT_MEMORY, "MmFindLocationForPageLookupTable() returning 0x%x\n", PageLookupTableMemAddress));
265
266 return PageLookupTableMemAddress;
267 }
268
269 VOID MmSortBiosMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap, ULONG MapCount)
270 {
271 ULONG Index;
272 ULONG LoopCount;
273 BIOS_MEMORY_MAP TempMapItem;
274
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++)
278 {
279 for (Index=0; Index<(MapCount-1); Index++)
280 {
281 if (BiosMemoryMap[Index].BaseAddress > BiosMemoryMap[Index+1].BaseAddress)
282 {
283 TempMapItem = BiosMemoryMap[Index];
284 BiosMemoryMap[Index] = BiosMemoryMap[Index+1];
285 BiosMemoryMap[Index+1] = TempMapItem;
286 }
287 }
288 }
289 }
290
291 VOID MmInitPageLookupTable(PVOID PageLookupTable, ULONG TotalPageCount, PBIOS_MEMORY_MAP BiosMemoryMap, ULONG MapCount)
292 {
293 ULONG MemoryMapStartPage;
294 ULONG MemoryMapEndPage;
295 ULONG MemoryMapPageCount;
296 ULONG MemoryMapPageAllocated;
297 ULONG PageLookupTableStartPage;
298 ULONG PageLookupTablePageCount;
299 ULONG Index;
300
301 DbgPrint((DPRINT_MEMORY, "MmInitPageLookupTable()\n"));
302
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);
307
308 for (Index=0; Index<MapCount; Index++)
309 {
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;
313
314 switch (BiosMemoryMap[Index].Type)
315 {
316 case BiosMemoryUsable:
317 MemoryMapPageAllocated = LoaderFree;
318 break;
319
320 case BiosMemoryAcpiReclaim:
321 case BiosMemoryAcpiNvs:
322 MemoryMapPageAllocated = LoaderSpecialMemory;
323 break;
324
325 default:
326 MemoryMapPageAllocated = LoaderSpecialMemory;
327 }
328 DbgPrint((DPRINT_MEMORY, "Marking pages as type %d: StartPage: %d PageCount: %d\n", MemoryMapPageAllocated, MemoryMapStartPage, MemoryMapPageCount));
329 MmMarkPagesInLookupTable(PageLookupTable, MemoryMapStartPage, MemoryMapPageCount, MemoryMapPageAllocated);
330 }
331
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);
337 }
338
339 VOID MmMarkPagesInLookupTable(PVOID PageLookupTable, ULONG StartPage, ULONG PageCount, TYPE_OF_MEMORY PageAllocated)
340 {
341 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
342 ULONG Index;
343
344 for (Index=StartPage; Index<(StartPage+PageCount); Index++)
345 {
346 #if 0
347 if ((Index <= (StartPage + 16)) || (Index >= (StartPage+PageCount-16)))
348 {
349 DbgPrint((DPRINT_MEMORY, "Index = %d StartPage = %d PageCount = %d\n", Index, StartPage, PageCount));
350 }
351 #endif
352 RealPageLookupTable[Index].PageAllocated = PageAllocated;
353 RealPageLookupTable[Index].PageAllocationLength = (PageAllocated != LoaderFree) ? 1 : 0;
354 }
355 DbgPrint((DPRINT_MEMORY, "MmMarkPagesInLookupTable() Done\n"));
356 }
357
358 VOID MmAllocatePagesInLookupTable(PVOID PageLookupTable, ULONG StartPage, ULONG PageCount, TYPE_OF_MEMORY MemoryType)
359 {
360 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
361 ULONG Index;
362
363 for (Index=StartPage; Index<(StartPage+PageCount); Index++)
364 {
365 RealPageLookupTable[Index].PageAllocated = MemoryType;
366 RealPageLookupTable[Index].PageAllocationLength = (Index == StartPage) ? PageCount : 0;
367 }
368 }
369
370 ULONG MmCountFreePagesInLookupTable(PVOID PageLookupTable, ULONG TotalPageCount)
371 {
372 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
373 ULONG Index;
374 ULONG FreePageCount;
375
376 FreePageCount = 0;
377 for (Index=0; Index<TotalPageCount; Index++)
378 {
379 if (RealPageLookupTable[Index].PageAllocated == LoaderFree)
380 {
381 FreePageCount++;
382 }
383 }
384
385 return FreePageCount;
386 }
387
388 ULONG MmFindAvailablePages(PVOID PageLookupTable, ULONG TotalPageCount, ULONG PagesNeeded, BOOLEAN FromEnd)
389 {
390 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
391 ULONG AvailablePagesSoFar;
392 ULONG Index;
393
394 if (LastFreePageHint > TotalPageCount)
395 {
396 LastFreePageHint = TotalPageCount;
397 }
398
399 AvailablePagesSoFar = 0;
400 if (FromEnd)
401 {
402 /* Allocate "high" (from end) pages */
403 for (Index=LastFreePageHint-1; Index>0; Index--)
404 {
405 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
406 {
407 AvailablePagesSoFar = 0;
408 continue;
409 }
410 else
411 {
412 AvailablePagesSoFar++;
413 }
414
415 if (AvailablePagesSoFar >= PagesNeeded)
416 {
417 return Index;
418 }
419 }
420 }
421 else
422 {
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++)
426 {
427 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
428 {
429 AvailablePagesSoFar = 0;
430 continue;
431 }
432 else
433 {
434 AvailablePagesSoFar++;
435 }
436
437 if (AvailablePagesSoFar >= PagesNeeded)
438 {
439 return Index - AvailablePagesSoFar + 1;
440 }
441 }
442 }
443
444 return 0;
445 }
446
447 ULONG MmFindAvailablePagesBeforePage(PVOID PageLookupTable, ULONG TotalPageCount, ULONG PagesNeeded, ULONG LastPage)
448 {
449 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
450 ULONG AvailablePagesSoFar;
451 ULONG Index;
452
453 if (LastPage > TotalPageCount)
454 {
455 return MmFindAvailablePages(PageLookupTable, TotalPageCount, PagesNeeded, TRUE);
456 }
457
458 AvailablePagesSoFar = 0;
459 for (Index=LastPage-1; Index>0; Index--)
460 {
461 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
462 {
463 AvailablePagesSoFar = 0;
464 continue;
465 }
466 else
467 {
468 AvailablePagesSoFar++;
469 }
470
471 if (AvailablePagesSoFar >= PagesNeeded)
472 {
473 return Index;
474 }
475 }
476
477 return 0;
478 }
479
480 VOID MmFixupSystemMemoryMap(PBIOS_MEMORY_MAP BiosMemoryMap, ULONG* MapCount)
481 {
482 UINT Index;
483 UINT Index2;
484
485 // Loop through each entry in the array
486 for (Index=0; Index<*MapCount; Index++)
487 {
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)
492 {
493 // Slide every entry after this down one
494 for (Index2=Index; Index2<(*MapCount - 1); Index2++)
495 {
496 BiosMemoryMap[Index2] = BiosMemoryMap[Index2 + 1];
497 }
498 (*MapCount)--;
499 Index--;
500 }
501 }
502 }
503
504 VOID MmUpdateLastFreePageHint(PVOID PageLookupTable, ULONG TotalPageCount)
505 {
506 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
507 ULONG Index;
508
509 for (Index=TotalPageCount-1; Index>0; Index--)
510 {
511 if (RealPageLookupTable[Index].PageAllocated == LoaderFree)
512 {
513 LastFreePageHint = Index + 1;
514 break;
515 }
516 }
517 }
518
519 BOOLEAN MmAreMemoryPagesAvailable(PVOID PageLookupTable, ULONG TotalPageCount, PVOID PageAddress, ULONG PageCount)
520 {
521 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
522 ULONG StartPage;
523 ULONG Index;
524
525 StartPage = MmGetPageNumberFromAddress(PageAddress);
526
527 // Make sure they aren't trying to go past the
528 // end of availabe memory
529 if ((StartPage + PageCount) > TotalPageCount)
530 {
531 return FALSE;
532 }
533
534 for (Index=StartPage; Index<(StartPage + PageCount); Index++)
535 {
536 // If this page is allocated then there obviously isn't
537 // memory availabe so return FALSE
538 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
539 {
540 return FALSE;
541 }
542 }
543
544 return TRUE;
545 }