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