[FREELDR]
[reactos.git] / reactos / boot / freeldr / freeldr / mm / meminit.c
1 /*
2 * FreeLoader
3 * Copyright (C) 2006-2008 Aleksey Bragin <aleksey@reactos.org>
4 * Copyright (C) 2006-2009 Hervé Poussineau <hpoussin@reactos.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <freeldr.h>
22 #include <debug.h>
23
24 DBG_DEFAULT_CHANNEL(MEMORY);
25
26 #if DBG
27 typedef struct
28 {
29 MEMORY_TYPE Type;
30 PCSTR TypeString;
31 } FREELDR_MEMORY_TYPE, *PFREELDR_MEMORY_TYPE;
32
33 FREELDR_MEMORY_TYPE MemoryTypeArray[] =
34 {
35 { MemoryMaximum, "Unknown memory" },
36 { MemoryExceptionBlock, "Exception block" },
37 { MemorySystemBlock, "System block" },
38 { MemoryFree, "Free memory" },
39 { MemoryBad, "Bad memory" },
40 { MemoryLoadedProgram, "Loaded program" },
41 { MemoryFirmwareTemporary, "Firmware temporary" },
42 { MemoryFirmwarePermanent, "Firmware permanent" },
43 { MemoryFreeContiguous, "Free contiguous memory" },
44 { MemorySpecialMemory, "Special memory" },
45 };
46 ULONG MemoryTypeCount = sizeof(MemoryTypeArray) / sizeof(MemoryTypeArray[0]);
47 #endif
48
49 PVOID PageLookupTableAddress = NULL;
50 ULONG TotalPagesInLookupTable = 0;
51 ULONG FreePagesInLookupTable = 0;
52 ULONG LastFreePageHint = 0;
53 ULONG MmLowestPhysicalPage = 0xFFFFFFFF;
54 ULONG MmHighestPhysicalPage = 0;
55
56 PMEMORY_DESCRIPTOR BiosMemoryMap;
57 ULONG BiosMemoryMapEntryCount;
58
59 extern ULONG_PTR MmHeapPointer;
60 extern ULONG_PTR MmHeapStart;
61
62 ULONG
63 AddMemoryDescriptor(
64 IN OUT PMEMORY_DESCRIPTOR List,
65 IN ULONG MaxCount,
66 IN PFN_NUMBER BasePage,
67 IN PFN_NUMBER PageCount,
68 IN MEMORY_TYPE MemoryType)
69 {
70 ULONG i, c;
71 PFN_NUMBER NextBase;
72 TRACE("AddMemoryDescriptor(0x%lx-0x%lx [0x%lx pages])\n",
73 BasePage, BasePage + PageCount, PageCount);
74
75 /* Scan through all existing descriptors */
76 for (i = 0, c = 0; (c < MaxCount) && (List[c].PageCount != 0); c++)
77 {
78 /* Count entries completely below the new range */
79 if (List[i].BasePage + List[i].PageCount <= BasePage) i++;
80 }
81
82 /* Check if the list is full */
83 if (c >= MaxCount) return c;
84
85 /* Is there an existing descriptor starting before the new range */
86 while ((i < c) && (List[i].BasePage <= BasePage))
87 {
88 /* The end of the existing one is the minimum for the new range */
89 NextBase = List[i].BasePage + List[i].PageCount;
90
91 /* Bail out, if everything is trimmed away */
92 if ((BasePage + PageCount) <= NextBase) return c;
93
94 /* Trim the naew range at the lower end */
95 PageCount -= (NextBase - BasePage);
96 BasePage = NextBase;
97
98 /* Go to the next entry and repeat */
99 i++;
100 }
101
102 ASSERT(PageCount > 0);
103
104 /* Are there still entries above? */
105 if (i < c)
106 {
107 /* Shift the following entries one up */
108 RtlMoveMemory(&List[i+1], &List[i], (c - i) * sizeof(List[0]));
109
110 /* Insert the new range */
111 List[i].BasePage = BasePage;
112 List[i].PageCount = min(PageCount, List[i+1].BasePage - BasePage);
113 List[i].MemoryType = MemoryType;
114 c++;
115
116 TRACE("Inserting at i=%ld: (0x%lx:0x%lx)\n",
117 i, List[i].BasePage, List[i].PageCount);
118
119 /* Check if the range was trimmed */
120 if (PageCount > List[i].PageCount)
121 {
122 /* Recursively process the trimmed part */
123 c = AddMemoryDescriptor(List,
124 MaxCount,
125 BasePage + List[i].PageCount,
126 PageCount - List[i].PageCount,
127 MemoryType);
128 }
129 }
130 else
131 {
132 /* We can simply add the range here */
133 TRACE("Adding i=%ld: (0x%lx:0x%lx)\n", i, BasePage, PageCount);
134 List[i].BasePage = BasePage;
135 List[i].PageCount = PageCount;
136 List[i].MemoryType = MemoryType;
137 c++;
138 }
139
140 /* Return the new count */
141 return c;
142 }
143
144 const MEMORY_DESCRIPTOR*
145 ArcGetMemoryDescriptor(const MEMORY_DESCRIPTOR* Current)
146 {
147 if (Current == NULL)
148 {
149 return BiosMemoryMap;
150 }
151 else
152 {
153 Current++;
154 if (Current->PageCount == 0) return NULL;
155 return Current;
156 }
157 }
158
159
160 BOOLEAN MmInitializeMemoryManager(VOID)
161 {
162 #if DBG
163 const MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
164 #endif
165
166 TRACE("Initializing Memory Manager.\n");
167
168 BiosMemoryMap = MachVtbl.GetMemoryMap(&BiosMemoryMapEntryCount);
169
170 #if DBG
171 // Dump the system memory map
172 TRACE("System Memory Map (Base Address, Length, Type):\n");
173 while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
174 {
175 TRACE("%x\t %x\t %s\n",
176 MemoryDescriptor->BasePage * MM_PAGE_SIZE,
177 MemoryDescriptor->PageCount * MM_PAGE_SIZE,
178 MmGetSystemMemoryMapTypeString(MemoryDescriptor->MemoryType));
179 }
180 #endif
181
182 // Find address for the page lookup table
183 TotalPagesInLookupTable = MmGetAddressablePageCountIncludingHoles();
184 PageLookupTableAddress = MmFindLocationForPageLookupTable(TotalPagesInLookupTable);
185 LastFreePageHint = MmHighestPhysicalPage;
186
187 if (PageLookupTableAddress == 0)
188 {
189 // If we get here then we probably couldn't
190 // find a contiguous chunk of memory big
191 // enough to hold the page lookup table
192 printf("Error initializing memory manager!\n");
193 return FALSE;
194 }
195
196 // Initialize the page lookup table
197 MmInitPageLookupTable(PageLookupTableAddress, TotalPagesInLookupTable);
198
199 {
200 ULONG Type, Index, PrevIndex = 0;
201 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTableAddress;
202
203 Type = RealPageLookupTable[0].PageAllocated;
204 for (Index = 1; Index < TotalPagesInLookupTable; Index++)
205 {
206 if ((RealPageLookupTable[Index].PageAllocated != Type) ||
207 (Index == TotalPagesInLookupTable - 1))
208 {
209 TRACE("Range: 0x%lx - 0x%lx Type=%d\n",
210 PrevIndex, Index - 1, Type);
211 Type = RealPageLookupTable[Index].PageAllocated;
212 PrevIndex = Index;
213 }
214 }
215 }
216
217
218 MmUpdateLastFreePageHint(PageLookupTableAddress, TotalPagesInLookupTable);
219
220 FreePagesInLookupTable = MmCountFreePagesInLookupTable(PageLookupTableAddress, TotalPagesInLookupTable);
221
222 MmInitializeHeap(PageLookupTableAddress);
223
224 TRACE("Memory Manager initialized. %d pages available.\n", FreePagesInLookupTable);
225
226
227 return TRUE;
228 }
229
230 #if DBG
231 PCSTR MmGetSystemMemoryMapTypeString(MEMORY_TYPE Type)
232 {
233 ULONG Index;
234
235 for (Index=1; Index<MemoryTypeCount; Index++)
236 {
237 if (MemoryTypeArray[Index].Type == Type)
238 {
239 return MemoryTypeArray[Index].TypeString;
240 }
241 }
242
243 return MemoryTypeArray[0].TypeString;
244 }
245 #endif
246
247 ULONG MmGetPageNumberFromAddress(PVOID Address)
248 {
249 return ((ULONG_PTR)Address) / MM_PAGE_SIZE;
250 }
251
252 ULONG MmGetAddressablePageCountIncludingHoles(VOID)
253 {
254 const MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
255 ULONG PageCount;
256
257 //
258 // Go through the whole memory map to get max address
259 //
260 while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
261 {
262 //
263 // Check if we got a higher end page address
264 //
265 if (MemoryDescriptor->BasePage + MemoryDescriptor->PageCount > MmHighestPhysicalPage)
266 {
267 //
268 // Yes, remember it if this is real memory
269 //
270 if (MemoryDescriptor->MemoryType == MemoryFree) MmHighestPhysicalPage = MemoryDescriptor->BasePage + MemoryDescriptor->PageCount;
271 }
272
273 //
274 // Check if we got a higher (usable) start page address
275 //
276 if (MemoryDescriptor->BasePage < MmLowestPhysicalPage)
277 {
278 //
279 // Yes, remember it if this is real memory
280 //
281 MmLowestPhysicalPage = MemoryDescriptor->BasePage;
282 }
283 }
284
285 TRACE("lo/hi %lx %lxn", MmLowestPhysicalPage, MmHighestPhysicalPage);
286 PageCount = MmHighestPhysicalPage - MmLowestPhysicalPage;
287 TRACE("MmGetAddressablePageCountIncludingHoles() returning 0x%x\n", PageCount);
288 return PageCount;
289 }
290
291 PVOID MmFindLocationForPageLookupTable(ULONG TotalPageCount)
292 {
293 const MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
294 ULONG PageLookupTableSize;
295 ULONG PageLookupTablePages;
296 ULONG PageLookupTableStartPage = 0;
297 PVOID PageLookupTableMemAddress = NULL;
298
299 //
300 // Calculate how much pages we need to keep the page lookup table
301 //
302 PageLookupTableSize = TotalPageCount * sizeof(PAGE_LOOKUP_TABLE_ITEM);
303 PageLookupTablePages = PageLookupTableSize / MM_PAGE_SIZE;
304
305 //
306 // Search the highest memory block big enough to contain lookup table
307 //
308 while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
309 {
310 //
311 // Is it suitable memory?
312 //
313 if (MemoryDescriptor->MemoryType != MemoryFree)
314 {
315 //
316 // No. Process next descriptor
317 //
318 continue;
319 }
320
321 //
322 // Is the block big enough?
323 //
324 if (MemoryDescriptor->PageCount < PageLookupTablePages)
325 {
326 //
327 // No. Process next descriptor
328 //
329 continue;
330 }
331
332 //
333 // Is it at a higher address than previous suitable address?
334 //
335 if (MemoryDescriptor->BasePage < PageLookupTableStartPage)
336 {
337 //
338 // No. Process next descriptor
339 //
340 continue;
341 }
342
343 //
344 // Can we use this address?
345 //
346 if (MemoryDescriptor->BasePage >= MM_MAX_PAGE)
347 {
348 //
349 // No. Process next descriptor
350 //
351 continue;
352 }
353
354 //
355 // Memory block is more suitable than the previous one
356 //
357 PageLookupTableStartPage = MemoryDescriptor->BasePage;
358 PageLookupTableMemAddress = (PVOID)((ULONG_PTR)
359 (MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) * MM_PAGE_SIZE
360 - PageLookupTableSize);
361 }
362
363 TRACE("MmFindLocationForPageLookupTable() returning 0x%x\n", PageLookupTableMemAddress);
364
365 return PageLookupTableMemAddress;
366 }
367
368 VOID MmInitPageLookupTable(PVOID PageLookupTable, ULONG TotalPageCount)
369 {
370 const MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
371 TYPE_OF_MEMORY MemoryMapPageAllocated;
372 ULONG PageLookupTableStartPage;
373 ULONG PageLookupTablePageCount;
374
375 TRACE("MmInitPageLookupTable()\n");
376
377 //
378 // Mark every page as allocated initially
379 // We will go through and mark pages again according to the memory map
380 // But this will mark any holes not described in the map as allocated
381 //
382 MmMarkPagesInLookupTable(PageLookupTable, MmLowestPhysicalPage, TotalPageCount, LoaderFirmwarePermanent);
383
384 //
385 // Parse the whole memory map
386 //
387 while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
388 {
389 TRACE("Got range: 0x%lx-0x%lx, type=%s\n",
390 MemoryDescriptor->BasePage,
391 MemoryDescriptor->BasePage + MemoryDescriptor->PageCount,
392 MmGetSystemMemoryMapTypeString(MemoryDescriptor->MemoryType));
393 //
394 // Convert ARC memory type to loader memory type
395 //
396 switch (MemoryDescriptor->MemoryType)
397 {
398 case MemoryFree:
399 {
400 //
401 // Allocatable memory
402 //
403 MemoryMapPageAllocated = LoaderFree;
404 break;
405 }
406 case MemoryFirmwarePermanent:
407 {
408 //
409 // Firmware permanent memory
410 //
411 MemoryMapPageAllocated = LoaderFirmwarePermanent;
412 break;
413 }
414 case MemoryFirmwareTemporary:
415 {
416 //
417 // Firmware temporary memory
418 //
419 MemoryMapPageAllocated = LoaderFirmwareTemporary;
420 break;
421 }
422 case MemoryLoadedProgram:
423 {
424 //
425 // Bootloader code
426 //
427 MemoryMapPageAllocated = LoaderLoadedProgram;
428 break;
429 }
430 case MemorySpecialMemory:
431 {
432 //
433 // OS Loader Stack
434 //
435 MemoryMapPageAllocated = LoaderOsloaderStack;
436 break;
437 }
438 default:
439 {
440 //
441 // Put something sensible here, which won't be overwritten
442 //
443 MemoryMapPageAllocated = LoaderSpecialMemory;
444 break;
445 }
446 }
447
448 //
449 // Mark used pages in the lookup table
450 //
451 TRACE("Marking pages as type %d: StartPage: %d PageCount: %d\n", MemoryMapPageAllocated, MemoryDescriptor->BasePage, MemoryDescriptor->PageCount);
452 MmMarkPagesInLookupTable(PageLookupTable, MemoryDescriptor->BasePage, MemoryDescriptor->PageCount, MemoryMapPageAllocated);
453 }
454
455 //
456 // Mark the pages that the lookup table occupies as reserved
457 //
458 PageLookupTableStartPage = MmGetPageNumberFromAddress(PageLookupTable);
459 PageLookupTablePageCount = MmGetPageNumberFromAddress((PVOID)((ULONG_PTR)PageLookupTable + ROUND_UP(TotalPageCount * sizeof(PAGE_LOOKUP_TABLE_ITEM), MM_PAGE_SIZE))) - PageLookupTableStartPage;
460 TRACE("Marking the page lookup table pages as reserved StartPage: %d PageCount: %d\n", PageLookupTableStartPage, PageLookupTablePageCount);
461 MmMarkPagesInLookupTable(PageLookupTable, PageLookupTableStartPage, PageLookupTablePageCount, LoaderFirmwareTemporary);
462 }
463
464 VOID MmMarkPagesInLookupTable(PVOID PageLookupTable, ULONG StartPage, ULONG PageCount, TYPE_OF_MEMORY PageAllocated)
465 {
466 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
467 ULONG Index;
468 TRACE("MmMarkPagesInLookupTable()\n");
469
470 StartPage -= MmLowestPhysicalPage;
471 for (Index=StartPage; Index<(StartPage+PageCount); Index++)
472 {
473 #if 0
474 if ((Index <= (StartPage + 16)) || (Index >= (StartPage+PageCount-16)))
475 {
476 TRACE("Index = %d StartPage = %d PageCount = %d\n", Index, StartPage, PageCount);
477 }
478 #endif
479 RealPageLookupTable[Index].PageAllocated = PageAllocated;
480 RealPageLookupTable[Index].PageAllocationLength = (PageAllocated != LoaderFree) ? 1 : 0;
481 }
482 TRACE("MmMarkPagesInLookupTable() Done\n");
483 }
484
485 VOID MmAllocatePagesInLookupTable(PVOID PageLookupTable, ULONG StartPage, ULONG PageCount, TYPE_OF_MEMORY MemoryType)
486 {
487 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
488 ULONG Index;
489
490 StartPage -= MmLowestPhysicalPage;
491 for (Index=StartPage; Index<(StartPage+PageCount); Index++)
492 {
493 RealPageLookupTable[Index].PageAllocated = MemoryType;
494 RealPageLookupTable[Index].PageAllocationLength = (Index == StartPage) ? PageCount : 0;
495 }
496 }
497
498 ULONG MmCountFreePagesInLookupTable(PVOID PageLookupTable, ULONG TotalPageCount)
499 {
500 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
501 ULONG Index;
502 ULONG FreePageCount;
503
504 FreePageCount = 0;
505 for (Index=0; Index<TotalPageCount; Index++)
506 {
507 if (RealPageLookupTable[Index].PageAllocated == LoaderFree)
508 {
509 FreePageCount++;
510 }
511 }
512
513 return FreePageCount;
514 }
515
516 ULONG MmFindAvailablePages(PVOID PageLookupTable, ULONG TotalPageCount, ULONG PagesNeeded, BOOLEAN FromEnd)
517 {
518 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
519 ULONG AvailablePagesSoFar;
520 ULONG Index;
521
522 if (LastFreePageHint > TotalPageCount)
523 {
524 LastFreePageHint = TotalPageCount;
525 }
526
527 AvailablePagesSoFar = 0;
528 if (FromEnd)
529 {
530 /* Allocate "high" (from end) pages */
531 for (Index=LastFreePageHint-1; Index>0; Index--)
532 {
533 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
534 {
535 AvailablePagesSoFar = 0;
536 continue;
537 }
538 else
539 {
540 AvailablePagesSoFar++;
541 }
542
543 if (AvailablePagesSoFar >= PagesNeeded)
544 {
545 return Index + MmLowestPhysicalPage;
546 }
547 }
548 }
549 else
550 {
551 TRACE("Alloc low memory, LastFreePageHint %d, TPC %d\n", LastFreePageHint, TotalPageCount);
552 /* Allocate "low" pages */
553 for (Index=1; Index < LastFreePageHint; Index++)
554 {
555 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
556 {
557 AvailablePagesSoFar = 0;
558 continue;
559 }
560 else
561 {
562 AvailablePagesSoFar++;
563 }
564
565 if (AvailablePagesSoFar >= PagesNeeded)
566 {
567 return Index - AvailablePagesSoFar + 1 + MmLowestPhysicalPage;
568 }
569 }
570 }
571
572 return 0;
573 }
574
575 ULONG MmFindAvailablePagesBeforePage(PVOID PageLookupTable, ULONG TotalPageCount, ULONG PagesNeeded, ULONG LastPage)
576 {
577 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
578 ULONG AvailablePagesSoFar;
579 ULONG Index;
580
581 if (LastPage > TotalPageCount)
582 {
583 return MmFindAvailablePages(PageLookupTable, TotalPageCount, PagesNeeded, TRUE);
584 }
585
586 AvailablePagesSoFar = 0;
587 for (Index=LastPage-1; Index>0; Index--)
588 {
589 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
590 {
591 AvailablePagesSoFar = 0;
592 continue;
593 }
594 else
595 {
596 AvailablePagesSoFar++;
597 }
598
599 if (AvailablePagesSoFar >= PagesNeeded)
600 {
601 return Index + MmLowestPhysicalPage;
602 }
603 }
604
605 return 0;
606 }
607
608 VOID MmUpdateLastFreePageHint(PVOID PageLookupTable, ULONG TotalPageCount)
609 {
610 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
611 ULONG Index;
612
613 for (Index=TotalPageCount-1; Index>0; Index--)
614 {
615 if (RealPageLookupTable[Index].PageAllocated == LoaderFree)
616 {
617 LastFreePageHint = Index + 1 + MmLowestPhysicalPage;
618 break;
619 }
620 }
621 }
622
623 BOOLEAN MmAreMemoryPagesAvailable(PVOID PageLookupTable, ULONG TotalPageCount, PVOID PageAddress, ULONG PageCount)
624 {
625 PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
626 ULONG StartPage;
627 ULONG Index;
628
629 StartPage = MmGetPageNumberFromAddress(PageAddress);
630
631 if (StartPage < MmLowestPhysicalPage) return FALSE;
632
633 StartPage -= MmLowestPhysicalPage;
634
635 // Make sure they aren't trying to go past the
636 // end of availabe memory
637 if ((StartPage + PageCount) > TotalPageCount)
638 {
639 return FALSE;
640 }
641
642 for (Index=StartPage; Index<(StartPage + PageCount); Index++)
643 {
644 // If this page is allocated then there obviously isn't
645 // memory availabe so return FALSE
646 if (RealPageLookupTable[Index].PageAllocated != LoaderFree)
647 {
648 return FALSE;
649 }
650 }
651
652 return TRUE;
653 }