- Major rewrite of Memory Descriptor List (MDL) implementation (moving it towards...
[reactos.git] / reactos / ntoskrnl / mm / mminit.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/mm/mminit.c
5 * PURPOSE: Memory Manager Initialization
6 * PROGRAMMERS:
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 PCHAR
18 MemType[] =
19 {
20 "ExceptionBlock ",
21 "SystemBlock ",
22 "Free ",
23 "Bad ",
24 "LoadedProgram ",
25 "FirmwareTemporary ",
26 "FirmwarePermanent ",
27 "OsloaderHeap ",
28 "OsloaderStack ",
29 "SystemCode ",
30 "HalCode ",
31 "BootDriver ",
32 "ConsoleInDriver ",
33 "ConsoleOutDriver ",
34 "StartupDpcStack ",
35 "StartupKernelStack",
36 "StartupPanicStack ",
37 "StartupPcrPage ",
38 "StartupPdrPage ",
39 "RegistryData ",
40 "MemoryData ",
41 "NlsData ",
42 "SpecialMemory ",
43 "BBTMemory ",
44 "LoaderReserve ",
45 "LoaderXIPRom "
46 };
47
48 BOOLEAN IsThisAnNtAsSystem = FALSE;
49 MM_SYSTEMSIZE MmSystemSize = MmSmallSystem;
50 PVOID MiNonPagedPoolStart;
51 ULONG MiNonPagedPoolLength;
52 ULONG MmBootImageSize;
53 ULONG MmNumberOfPhysicalPages, MmHighestPhysicalPage, MmLowestPhysicalPage;
54 ULONG_PTR MiKSeg0Start, MiKSeg0End;
55 ULONG_PTR MmPfnDatabaseEnd;
56 PMEMORY_ALLOCATION_DESCRIPTOR MiFreeDescriptor;
57 MEMORY_ALLOCATION_DESCRIPTOR MiFreeDescriptorOrg;
58 ULONG MmUserProbeAddress = 0;
59 PVOID MmHighestUserAddress = NULL;
60 PBOOLEAN Mm64BitPhysicalAddress = FALSE;
61 PVOID MmSystemRangeStart = NULL;
62 ULONG MmReadClusterSize;
63 MM_STATS MmStats;
64 PMMSUPPORT MmKernelAddressSpace;
65 extern KMUTANT MmSystemLoadLock;
66 extern HANDLE MpwThreadHandle;
67 extern BOOLEAN MpwThreadShouldTerminate;
68 extern KEVENT MpwThreadEvent;
69 BOOLEAN MiDbgEnableMdDump =
70 #ifdef _ARM_
71 TRUE;
72 #else
73 FALSE;
74 #endif
75
76 /* PRIVATE FUNCTIONS *********************************************************/
77
78 VOID
79 NTAPI
80 MiShutdownMemoryManager(VOID)
81 {
82 #if 0
83 ULONG PagesWritten;
84 PETHREAD Thread;
85
86 /* Ask MPW thread to shutdown */
87 MpwThreadShouldTerminate = TRUE;
88 KeSetEvent(&MpwThreadEvent, IO_NO_INCREMENT, FALSE);
89
90 /* Wait for it */
91 ObReferenceObjectByHandle(MpwThreadHandle,
92 THREAD_ALL_ACCESS,
93 PsThreadType,
94 KernelMode,
95 (PVOID*)&Thread,
96 NULL);
97
98 KeWaitForSingleObject(Thread,
99 Executive,
100 KernelMode,
101 FALSE,
102 NULL);
103
104 ObDereferenceObject(Thread);
105
106 /* Check if there are any dirty pages, and flush them.
107 There will be no other chance to do this later, since filesystems
108 are going to be shut down. */
109 CcRosFlushDirtyPages(128, &PagesWritten);
110 #endif
111 }
112
113 VOID
114 INIT_FUNCTION
115 NTAPI
116 MiInitSystemMemoryAreas()
117 {
118 PVOID BaseAddress;
119 PHYSICAL_ADDRESS BoundaryAddressMultiple;
120 PMEMORY_AREA MArea;
121 BoundaryAddressMultiple.QuadPart = 0;
122
123 //
124 // First initialize the page table and hyperspace memory areas
125 //
126 MiInitPageDirectoryMap();
127
128 //
129 // Next, the KPCR
130 //
131 BaseAddress = (PVOID)PCR;
132 MmCreateMemoryArea(MmGetKernelAddressSpace(),
133 MEMORY_AREA_SYSTEM | MEMORY_AREA_STATIC,
134 &BaseAddress,
135 PAGE_SIZE * KeNumberProcessors,
136 PAGE_READWRITE,
137 &MArea,
138 TRUE,
139 0,
140 BoundaryAddressMultiple);
141
142 //
143 // Now the KUSER_SHARED_DATA
144 //
145 BaseAddress = (PVOID)KI_USER_SHARED_DATA;
146 MmCreateMemoryArea(MmGetKernelAddressSpace(),
147 MEMORY_AREA_SYSTEM | MEMORY_AREA_STATIC,
148 &BaseAddress,
149 PAGE_SIZE,
150 PAGE_READWRITE,
151 &MArea,
152 TRUE,
153 0,
154 BoundaryAddressMultiple);
155 }
156
157 VOID
158 NTAPI
159 MiCountFreePagesInLoaderBlock(PLOADER_PARAMETER_BLOCK LoaderBlock)
160 {
161 PLIST_ENTRY NextEntry;
162 PMEMORY_ALLOCATION_DESCRIPTOR Md;
163 ULONG FreePages = 0;
164
165 for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
166 NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
167 NextEntry = NextEntry->Flink)
168 {
169 Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
170
171 /* Skip invisible memory */
172 if ((Md->MemoryType != LoaderFirmwarePermanent) &&
173 (Md->MemoryType != LoaderSpecialMemory) &&
174 (Md->MemoryType != LoaderHALCachedMemory) &&
175 (Md->MemoryType != LoaderBBTMemory))
176 {
177 /* Check if BURNMEM was used */
178 if (Md->MemoryType != LoaderBad)
179 {
180 /* Count this in the total of pages */
181 MmNumberOfPhysicalPages += Md->PageCount;
182 }
183
184 /* Check if this is the new lowest page */
185 if (Md->BasePage < MmLowestPhysicalPage)
186 {
187 /* Update the lowest page */
188 MmLowestPhysicalPage = Md->BasePage;
189 }
190
191 /* Check if this is the new highest page */
192 if ((Md->BasePage + Md->PageCount) > MmHighestPhysicalPage)
193 {
194 /* Update the highest page */
195 MmHighestPhysicalPage = Md->BasePage + Md->PageCount - 1;
196 }
197
198 /* Check if this is free memory */
199 if ((Md->MemoryType == LoaderFree) ||
200 (Md->MemoryType == LoaderLoadedProgram) ||
201 (Md->MemoryType == LoaderFirmwareTemporary) ||
202 (Md->MemoryType == LoaderOsloaderStack))
203 {
204 /* Check if this is the largest memory descriptor */
205 if (Md->PageCount > FreePages)
206 {
207 /* For now, it is */
208 FreePages = Md->PageCount;
209 MiFreeDescriptor = Md;
210 }
211 }
212 }
213 }
214
215 /* Save original values of the free descriptor, since it'll be
216 altered by early allocations */
217 MiFreeDescriptorOrg = *MiFreeDescriptor;
218 }
219
220 VOID
221 NTAPI
222 MiDbgKernelLayout(VOID)
223 {
224 DPRINT1("%8s%12s\t\t%s\n", "Start", "End", "Type");
225 DPRINT1("0x%p - 0x%p\t%s\n",
226 KSEG0_BASE, MiKSeg0Start,
227 "Undefined region");
228 DPRINT1("0x%p - 0x%p\t%s\n",
229 MiKSeg0Start, MiKSeg0End,
230 "FreeLDR Kernel mapping region");
231 DPRINT1("0x%p - 0x%p\t%s\n",
232 MmPfnDatabase, MmPfnDatabaseEnd,
233 "PFN Database region");
234 if (MmPfnDatabaseEnd != (ULONG_PTR)MiNonPagedPoolStart)
235 DPRINT1("0x%p - 0x%p\t%s\n",
236 MmPfnDatabaseEnd, MiNonPagedPoolStart,
237 "Remaining FreeLDR mapping");
238 DPRINT1("0x%p - 0x%p\t%s\n",
239 MiNonPagedPoolStart, (ULONG_PTR)MiNonPagedPoolStart + MiNonPagedPoolLength,
240 "Non paged pool region");
241 DPRINT1("0x%p - 0x%p\t%s\n",
242 MmPagedPoolBase, (ULONG_PTR)MmPagedPoolBase + MmPagedPoolSize,
243 "Paged pool region");
244 }
245
246 VOID
247 NTAPI
248 MiDbgDumpMemoryDescriptors(VOID)
249 {
250 PLIST_ENTRY NextEntry;
251 PMEMORY_ALLOCATION_DESCRIPTOR Md;
252 ULONG TotalPages = 0;
253
254 DPRINT1("Base\t\tLength\t\tType\n");
255 for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
256 NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
257 NextEntry = NextEntry->Flink)
258 {
259 Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
260 DPRINT1("%08lX\t%08lX\t%s\n", Md->BasePage, Md->PageCount, MemType[Md->MemoryType]);
261 TotalPages += Md->PageCount;
262 }
263
264 DPRINT1("Total: %08lX (%d MB)\n", TotalPages, (TotalPages * PAGE_SIZE) / 1024 / 1024);
265 }
266
267 ULONG_PTR
268 NTAPI
269 MiGetLastKernelAddress(VOID)
270 {
271 PLIST_ENTRY NextEntry;
272 PMEMORY_ALLOCATION_DESCRIPTOR Md;
273 ULONG_PTR LastKrnlPhysAddr = 0;
274
275 for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
276 NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
277 NextEntry = NextEntry->Flink)
278 {
279 Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
280
281 if (Md->MemoryType != LoaderFree &&
282 Md->MemoryType != LoaderFirmwareTemporary &&
283 Md->MemoryType != LoaderSpecialMemory)
284 {
285 if (Md->BasePage+Md->PageCount > LastKrnlPhysAddr)
286 LastKrnlPhysAddr = Md->BasePage+Md->PageCount;
287 }
288 }
289
290 /* Convert to a physical address */
291 return LastKrnlPhysAddr << PAGE_SHIFT;
292 }
293
294
295 VOID
296 NTAPI
297 MiInitHyperSpace(VOID);
298
299 NTSTATUS
300 NTAPI
301 MmArmInitSystem(IN ULONG Phase,
302 IN PLOADER_PARAMETER_BLOCK LoaderBlock);
303
304 VOID
305 INIT_FUNCTION
306 NTAPI
307 MmInit1(VOID)
308 {
309 PLDR_DATA_TABLE_ENTRY LdrEntry;
310 ULONG Dummy[2];
311
312 /* Dump memory descriptors */
313 if (MiDbgEnableMdDump) MiDbgDumpMemoryDescriptors();
314
315 /* Get the size of FreeLDR's image allocations */
316 MmBootImageSize = KeLoaderBlock->Extension->LoaderPagesSpanned;
317 MmBootImageSize *= PAGE_SIZE;
318
319 /* Set memory limits */
320 MmSystemRangeStart = (PVOID)KSEG0_BASE;
321 MmUserProbeAddress = (ULONG_PTR)MmSystemRangeStart - 0x10000;
322 MmHighestUserAddress = (PVOID)(MmUserProbeAddress - 1);
323 DPRINT("MmSystemRangeStart: %08x\n", MmSystemRangeStart);
324 DPRINT("MmUserProbeAddress: %08x\n", MmUserProbeAddress);
325 DPRINT("MmHighestUserAddress:%08x\n", MmHighestUserAddress);
326
327 /* Initialize memory managment statistics */
328 RtlZeroMemory(&MmStats, sizeof(MmStats));
329
330 /* Count RAM */
331 MiCountFreePagesInLoaderBlock(KeLoaderBlock);
332 DbgPrint("Used memory %dKb\n", (MmNumberOfPhysicalPages * PAGE_SIZE) / 1024);
333
334 /* Initialize the kernel address space */
335 MmInitializeHandBuiltProcess(PsGetCurrentProcess(), Dummy);
336 MmKernelAddressSpace = MmGetCurrentAddressSpace();
337 MmInitGlobalKernelPageDirectory();
338
339 /* Get kernel address boundaries */
340 LdrEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
341 LDR_DATA_TABLE_ENTRY,
342 InLoadOrderLinks);
343 MiKSeg0Start = (ULONG_PTR)LdrEntry->DllBase | KSEG0_BASE;
344 MiKSeg0End = PAGE_ROUND_UP(MiGetLastKernelAddress() | KSEG0_BASE);
345
346 /* We'll put the PFN array right after the loaded modules */
347 MmPfnDatabase = (PVOID)MiKSeg0End;
348 MmPfnDatabaseEnd = (ULONG_PTR)MmPfnDatabase + (MmHighestPhysicalPage * sizeof(MMPFN));
349 MmPfnDatabaseEnd = PAGE_ROUND_UP(MmPfnDatabaseEnd);
350
351 /*
352 * FreeLDR maps 6MB starting at the kernel base address, followed by the
353 * PFN database. If the PFN database doesn't go over the FreeLDR allocation
354 * then choose the end of the FreeLDR block. If it does go past the FreeLDR
355 * allocation, then choose the next PAGE_SIZE boundary.
356 */
357 if ((ULONG_PTR)MmPfnDatabaseEnd < (MiKSeg0Start + 0x600000))
358 {
359 /* Use the first memory following FreeLDR's 6MB mapping */
360 MiNonPagedPoolStart = (PVOID)((ULONG_PTR)MiKSeg0Start + 0x600000);
361 }
362 else
363 {
364 /* Use the next free available page */
365 MiNonPagedPoolStart = (PVOID)MmPfnDatabaseEnd;
366 }
367
368 /* Length of non-paged pool */
369 MiNonPagedPoolLength = MM_NONPAGED_POOL_SIZE;
370
371 /* Put the paged pool after the non-paged pool */
372 MmPagedPoolBase = (PVOID)PAGE_ROUND_UP((ULONG_PTR)MiNonPagedPoolStart +
373 MiNonPagedPoolLength);
374 MmPagedPoolSize = MM_PAGED_POOL_SIZE;
375
376 /* Dump kernel memory layout */
377 MiDbgKernelLayout();
378
379 /* Intialize system memory areas */
380 MiInitSystemMemoryAreas();
381
382 /* Initialize the page list */
383 MmInitializePageList();
384
385 //
386 // Initialize ARMĀ³ in phase 0
387 //
388 MmArmInitSystem(0, KeLoaderBlock);
389
390 /* Initialize nonpaged pool */
391 MiInitializeNonPagedPool();
392
393 /* Initialize paged pool */
394 MmInitializePagedPool();
395
396 /* Initialize working sets */
397 MmInitializeMemoryConsumer(MC_USER, MmTrimUserMemory);
398
399 //
400 // Initialize ARMĀ³ in phase 1
401 //
402 MmArmInitSystem(1, KeLoaderBlock);
403 }
404
405 BOOLEAN
406 NTAPI
407 MmInitSystem(IN ULONG Phase,
408 IN PLOADER_PARAMETER_BLOCK LoaderBlock)
409 {
410 if (Phase == 0)
411 {
412 /* Initialize Mm bootstrap */
413 MmInit1();
414
415 /* Initialize the Loader Lock */
416 KeInitializeMutant(&MmSystemLoadLock, FALSE);
417
418 /* Reload boot drivers */
419 MiReloadBootLoadedDrivers(LoaderBlock);
420
421 /* Initialize the loaded module list */
422 MiInitializeLoadedModuleList(LoaderBlock);
423
424 /* We're done, for now */
425 DPRINT("Mm0: COMPLETE\n");
426 }
427 else if (Phase == 1)
428 {
429 MmInitializeRmapList();
430 MmInitializePageOp();
431 MmInitSectionImplementation();
432 MmInitPagingFile();
433 MmCreatePhysicalMemorySection();
434
435 /* Setup shared user data settings that NT does as well */
436 ASSERT(SharedUserData->NumberOfPhysicalPages == 0);
437 SharedUserData->NumberOfPhysicalPages = MmStats.NrTotalPages;
438 SharedUserData->LargePageMinimum = 0;
439
440 /* For now, we assume that we're always Workstation */
441 SharedUserData->NtProductType = NtProductWinNt;
442 }
443 else if (Phase == 2)
444 {
445 /*
446 * Unmap low memory
447 */
448 MiInitBalancerThread();
449
450 /*
451 * Initialise the modified page writer.
452 */
453 MmInitMpwThread();
454
455 /* Initialize the balance set manager */
456 MmInitBsmThread();
457
458 /* FIXME: Read parameters from memory */
459 }
460
461 return TRUE;
462 }
463
464
465 /* PUBLIC FUNCTIONS **********************************************************/
466
467 /*
468 * @implemented
469 */
470 BOOLEAN
471 NTAPI
472 MmIsThisAnNtAsSystem(VOID)
473 {
474 return IsThisAnNtAsSystem;
475 }
476
477 /*
478 * @implemented
479 */
480 MM_SYSTEMSIZE
481 NTAPI
482 MmQuerySystemSize(VOID)
483 {
484 return MmSystemSize;
485 }