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