xref: /reactos/boot/freeldr/freeldr/ntldr/wlmemory.c (revision dd6c1c88)
1 /*
2  * PROJECT:         EFI Windows Loader
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            boot/freeldr/freeldr/windows/wlmemory.c
5  * PURPOSE:         Memory related routines
6  * PROGRAMMERS:     Aleksey Bragin (aleksey@reactos.org)
7  */
8 
9 /* INCLUDES ***************************************************************/
10 
11 #include <freeldr.h>
12 #include "winldr.h"
13 
14 #include <debug.h>
15 DBG_DEFAULT_CHANNEL(WINDOWS);
16 
17 extern ULONG LoaderPagesSpanned;
18 
19 static const PCSTR MemTypeDesc[] = {
20     "ExceptionBlock    ", // ?
21     "SystemBlock       ", // ?
22     "Free              ",
23     "Bad               ", // used
24     "LoadedProgram     ", // == Free
25     "FirmwareTemporary ", // == Free
26     "FirmwarePermanent ", // == Bad
27     "OsloaderHeap      ", // used
28     "OsloaderStack     ", // == Free
29     "SystemCode        ",
30     "HalCode           ",
31     "BootDriver        ", // not used
32     "ConsoleInDriver   ", // ?
33     "ConsoleOutDriver  ", // ?
34     "StartupDpcStack   ", // ?
35     "StartupKernelStack", // ?
36     "StartupPanicStack ", // ?
37     "StartupPcrPage    ", // ?
38     "StartupPdrPage    ", // ?
39     "RegistryData      ", // used
40     "MemoryData        ", // not used
41     "NlsData           ", // used
42     "SpecialMemory     ", // == Bad
43     "BBTMemory         " // == Bad
44     };
45 
46 static VOID
47 WinLdrInsertDescriptor(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
48                        IN PMEMORY_ALLOCATION_DESCRIPTOR NewDescriptor);
49 
50 extern PFREELDR_MEMORY_DESCRIPTOR BiosMemoryMap;
51 extern ULONG BiosMemoryMapEntryCount;
52 extern PFN_NUMBER MmLowestPhysicalPage;
53 extern PFN_NUMBER MmHighestPhysicalPage;
54 
55 /* GLOBALS ***************************************************************/
56 
57 MEMORY_ALLOCATION_DESCRIPTOR *Mad;
58 ULONG MadCount = 0;
59 /* 200 MADs fit into 1 page, that should really be enough! */
60 #define MAX_MAD_COUNT 200
61 
62 /* FUNCTIONS **************************************************************/
63 
64 VOID
MempAddMemoryBlock(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,PFN_NUMBER BasePage,PFN_NUMBER PageCount,ULONG Type)65 MempAddMemoryBlock(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
66                    PFN_NUMBER BasePage,
67                    PFN_NUMBER PageCount,
68                    ULONG Type)
69 {
70     TRACE("MempAddMemoryBlock(BasePage=0x%lx, PageCount=0x%lx, Type=%ld)\n",
71           BasePage, PageCount, Type);
72 
73     /* Check for memory block after 4GB - we don't support it yet
74        Note: Even last page before 4GB limit is not supported */
75     if (BasePage >= MM_MAX_PAGE)
76     {
77         /* Just skip this, without even adding to MAD list */
78         return;
79     }
80 
81     /* Check if last page is after 4GB limit and shorten this block if needed */
82     if (BasePage + PageCount > MM_MAX_PAGE)
83     {
84         /* Shorten this block */
85         PageCount = MM_MAX_PAGE - BasePage;
86     }
87 
88     /* Check if we have slots left */
89     if (MadCount >= MAX_MAD_COUNT)
90     {
91         ERR("Error: no MAD slots left!\n");
92         return;
93     }
94 
95     /* Set Base page, page count and type */
96     Mad[MadCount].BasePage = BasePage;
97     Mad[MadCount].PageCount = PageCount;
98     Mad[MadCount].MemoryType = Type;
99 
100     /* Add descriptor */
101     WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]);
102     MadCount++;
103 }
104 
105 VOID
MempSetupPagingForRegion(PFN_NUMBER BasePage,PFN_NUMBER PageCount,ULONG Type)106 MempSetupPagingForRegion(
107     PFN_NUMBER BasePage,
108     PFN_NUMBER PageCount,
109     ULONG Type)
110 {
111     BOOLEAN Status = TRUE;
112 
113     TRACE("MempSetupPagingForRegion(BasePage=0x%lx, PageCount=0x%lx, Type=%ld)\n",
114           BasePage, PageCount, Type);
115 
116     /* Make sure we don't map too high */
117     if (BasePage + PageCount > LoaderPagesSpanned) return;
118 
119     switch (Type)
120     {
121         /* Pages used by the loader */
122         case LoaderLoadedProgram:
123         case LoaderOsloaderStack:
124         case LoaderFirmwareTemporary:
125             /* Map these pages into user mode */
126             Status = MempSetupPaging(BasePage, PageCount, FALSE);
127             break;
128 
129         /* Pages used by the kernel */
130         case LoaderExceptionBlock:
131         case LoaderSystemBlock:
132         case LoaderFirmwarePermanent:
133         case LoaderSystemCode:
134         case LoaderHalCode:
135         case LoaderBootDriver:
136         case LoaderConsoleInDriver:
137         case LoaderConsoleOutDriver:
138         case LoaderStartupDpcStack:
139         case LoaderStartupKernelStack:
140         case LoaderStartupPanicStack:
141         case LoaderStartupPcrPage:
142         case LoaderStartupPdrPage:
143         case LoaderRegistryData:
144         case LoaderMemoryData:
145         case LoaderNlsData:
146         case LoaderXIPRom:
147         case LoaderOsloaderHeap: // FIXME
148             /* Map these pages into kernel mode */
149             Status = MempSetupPaging(BasePage, PageCount, TRUE);
150             break;
151 
152         /* Pages not in use */
153         case LoaderFree:
154         case LoaderBad:
155             break;
156 
157         /* Invisible to kernel */
158         case LoaderSpecialMemory:
159         case LoaderHALCachedMemory:
160         case LoaderBBTMemory:
161             break;
162 
163         // FIXME: not known (not used anyway)
164         case LoaderReserve:
165         case LoaderLargePageFiller:
166         case LoaderErrorLogMemory:
167             break;
168     }
169 
170     if (!Status)
171     {
172         ERR("Error during MempSetupPaging\n");
173     }
174 }
175 
176 #ifdef _M_ARM
177 #define PKTSS PVOID
178 #endif
179 
180 BOOLEAN
WinLdrSetupMemoryLayout(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock)181 WinLdrSetupMemoryLayout(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock)
182 {
183     PFN_NUMBER i, PagesCount, MemoryMapSizeInPages, NoEntries;
184     PFN_NUMBER LastPageIndex, MemoryMapStartPage;
185     PPAGE_LOOKUP_TABLE_ITEM MemoryMap;
186     ULONG LastPageType;
187     //PKTSS Tss;
188     BOOLEAN Status;
189 
190     /* Cleanup heap */
191     FrLdrHeapCleanupAll();
192 
193     //
194     // Creating a suitable memory map for Windows can be tricky, so let's
195     // give a few advices:
196     // 1) One must not map the whole available memory pages to PDE!
197     //    Map only what's needed - 16Mb, 24Mb, 32Mb max I think,
198     //    thus occupying 4, 6 or 8 PDE entries for identical mapping,
199     //    the same quantity for KSEG0_BASE mapping, one more entry for
200     //    hyperspace and one more entry for HAL physical pages mapping.
201     // 2) Memory descriptors must map *the whole* physical memory
202     //    showing any memory above 16/24/32 as FirmwareTemporary
203     //
204     // 3) Overall memory blocks count must not exceed 30 (?? why?)
205     //
206 
207     //
208     // During MmInitMachineDependent, the kernel zeroes PDE at the following address
209     // 0xC0300000 - 0xC03007FC
210     //
211     // Then it finds the best place for non-paged pool:
212     // StartPde C0300F70, EndPde C0300FF8, NumberOfPages C13, NextPhysPage 3AD
213     //
214 
215     // Allocate memory for memory allocation descriptors
216     Mad = MmAllocateMemoryWithType(sizeof(MEMORY_ALLOCATION_DESCRIPTOR) * MAX_MAD_COUNT,
217                                    LoaderMemoryData);
218 
219     // Setup an entry for each descriptor
220     MemoryMap = MmGetMemoryMap(&NoEntries);
221     if (MemoryMap == NULL)
222     {
223         UiMessageBox("Can not retrieve the current memory map.");
224         return FALSE;
225     }
226 
227     // Calculate parameters of the memory map
228     MemoryMapStartPage = (ULONG_PTR)MemoryMap >> MM_PAGE_SHIFT;
229     MemoryMapSizeInPages = (NoEntries * sizeof(PAGE_LOOKUP_TABLE_ITEM) + MM_PAGE_SIZE - 1) / MM_PAGE_SIZE;
230 
231     TRACE("Got memory map with %d entries\n", NoEntries);
232 
233     // Always map first page of memory
234     Status = MempSetupPaging(0, 1, FALSE);
235     if (!Status)
236     {
237         ERR("Error during MempSetupPaging of first page\n");
238         return FALSE;
239     }
240 
241     /* Before creating the map, we need to map pages to kernel mode */
242     LastPageIndex = 1;
243     LastPageType = MemoryMap[1].PageAllocated;
244     for (i = 2; i < NoEntries; i++)
245     {
246         if ((MemoryMap[i].PageAllocated != LastPageType) ||
247             (i == NoEntries - 1))
248         {
249             MempSetupPagingForRegion(LastPageIndex, i - LastPageIndex, LastPageType);
250             LastPageIndex = i;
251             LastPageType = MemoryMap[i].PageAllocated;
252         }
253     }
254 
255     // Construct a good memory map from what we've got,
256     // but mark entries which the memory allocation bitmap takes
257     // as free entries (this is done in order to have the ability
258     // to place mem alloc bitmap outside lower 16Mb zone)
259     PagesCount = 1;
260     LastPageIndex = 0;
261     LastPageType = MemoryMap[0].PageAllocated;
262     for (i = 1; i < NoEntries; i++)
263     {
264         // Check if its memory map itself
265         if (i >= MemoryMapStartPage &&
266             i < (MemoryMapStartPage+MemoryMapSizeInPages))
267         {
268             // Exclude it if current page belongs to the memory map
269             MemoryMap[i].PageAllocated = LoaderFree;
270         }
271 
272         // Process entry
273         if (MemoryMap[i].PageAllocated == LastPageType &&
274             (i != NoEntries-1) )
275         {
276             PagesCount++;
277         }
278         else
279         {
280             // Add the resulting region
281             MempAddMemoryBlock(LoaderBlock, LastPageIndex, PagesCount, LastPageType);
282 
283             // Reset our counter vars
284             LastPageIndex = i;
285             LastPageType = MemoryMap[i].PageAllocated;
286             PagesCount = 1;
287         }
288     }
289 
290     // TEMP, DEBUG!
291     // adding special reserved memory zones for vmware workstation
292 #if 0
293     {
294         Mad[MadCount].BasePage = 0xfec00;
295         Mad[MadCount].PageCount = 0x10;
296         Mad[MadCount].MemoryType = LoaderSpecialMemory;
297         WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]);
298         MadCount++;
299 
300         Mad[MadCount].BasePage = 0xfee00;
301         Mad[MadCount].PageCount = 0x1;
302         Mad[MadCount].MemoryType = LoaderSpecialMemory;
303         WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]);
304         MadCount++;
305 
306         Mad[MadCount].BasePage = 0xfffe0;
307         Mad[MadCount].PageCount = 0x20;
308         Mad[MadCount].MemoryType = LoaderSpecialMemory;
309         WinLdrInsertDescriptor(LoaderBlock, &Mad[MadCount]);
310         MadCount++;
311     }
312 #endif
313 
314     /* Now we need to add high descriptors from the bios memory map */
315     for (i = 0; i < BiosMemoryMapEntryCount; i++)
316     {
317         /* Check if its higher than the lookup table */
318         if (BiosMemoryMap->BasePage > MmHighestPhysicalPage)
319         {
320             /* Copy this descriptor */
321             MempAddMemoryBlock(LoaderBlock,
322                                BiosMemoryMap->BasePage,
323                                BiosMemoryMap->PageCount,
324                                BiosMemoryMap->MemoryType);
325         }
326     }
327 
328     TRACE("MadCount: %d\n", MadCount);
329 
330     WinLdrpDumpMemoryDescriptors(LoaderBlock); //FIXME: Delete!
331 
332     // Map our loader image, so we can continue running
333     /*Status = MempSetupPaging(OsLoaderBase >> MM_PAGE_SHIFT, OsLoaderSize >> MM_PAGE_SHIFT);
334     if (!Status)
335     {
336         UiMessageBox("Error during MempSetupPaging.");
337         return;
338     }*/
339 
340     // Fill the memory descriptor list and
341     //PrepareMemoryDescriptorList();
342     TRACE("Memory Descriptor List prepared, printing PDE\n");
343     List_PaToVa(&LoaderBlock->MemoryDescriptorListHead);
344 
345 #if DBG
346     MempDump();
347 #endif
348 
349     return TRUE;
350 }
351 
352 // Two special things this func does: it sorts descriptors,
353 // and it merges free ones
354 static VOID
WinLdrInsertDescriptor(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,IN PMEMORY_ALLOCATION_DESCRIPTOR NewDescriptor)355 WinLdrInsertDescriptor(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
356                        IN PMEMORY_ALLOCATION_DESCRIPTOR NewDescriptor)
357 {
358     PLIST_ENTRY ListHead = &LoaderBlock->MemoryDescriptorListHead;
359     PLIST_ENTRY PreviousEntry, NextEntry;
360     PMEMORY_ALLOCATION_DESCRIPTOR PreviousDescriptor = NULL, NextDescriptor = NULL;
361 
362     TRACE("BP=0x%X PC=0x%X %s\n", NewDescriptor->BasePage,
363         NewDescriptor->PageCount, MemTypeDesc[NewDescriptor->MemoryType]);
364 
365     /* Find a place where to insert the new descriptor to */
366     PreviousEntry = ListHead;
367     NextEntry = ListHead->Flink;
368     while (NextEntry != ListHead)
369     {
370         NextDescriptor = CONTAINING_RECORD(NextEntry,
371             MEMORY_ALLOCATION_DESCRIPTOR,
372             ListEntry);
373         if (NewDescriptor->BasePage < NextDescriptor->BasePage)
374             break;
375 
376         PreviousEntry = NextEntry;
377         PreviousDescriptor = NextDescriptor;
378         NextEntry = NextEntry->Flink;
379     }
380 
381     /* Don't forget about merging free areas */
382     if (NewDescriptor->MemoryType != LoaderFree)
383     {
384         /* Just insert, nothing to merge */
385         InsertHeadList(PreviousEntry, &NewDescriptor->ListEntry);
386     }
387     else
388     {
389         /* Previous block also free? */
390         if ((PreviousEntry != ListHead) && (PreviousDescriptor->MemoryType == LoaderFree) &&
391             ((PreviousDescriptor->BasePage + PreviousDescriptor->PageCount) ==
392             NewDescriptor->BasePage))
393         {
394             /* Just enlarge previous descriptor's PageCount */
395             PreviousDescriptor->PageCount += NewDescriptor->PageCount;
396             NewDescriptor = PreviousDescriptor;
397         }
398         else
399         {
400             /* Nope, just insert */
401             InsertHeadList(PreviousEntry, &NewDescriptor->ListEntry);
402         }
403 
404         /* Next block is free ?*/
405         if ((NextEntry != ListHead) &&
406             (NextDescriptor->MemoryType == LoaderFree) &&
407             ((NewDescriptor->BasePage + NewDescriptor->PageCount) == NextDescriptor->BasePage))
408         {
409             /* Enlarge next descriptor's PageCount */
410             NewDescriptor->PageCount += NextDescriptor->PageCount;
411             RemoveEntryList(&NextDescriptor->ListEntry);
412         }
413     }
414 
415     return;
416 }
417