Merge trunk head (r41474)
[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 PHYSICAL_ADDRESS MmSharedDataPagePhysicalAddress;
51 PVOID MiNonPagedPoolStart;
52 ULONG MiNonPagedPoolLength;
53 ULONG MmBootImageSize;
54 ULONG MmNumberOfPhysicalPages, MmHighestPhysicalPage, MmLowestPhysicalPage;
55 ULONG_PTR MiKSeg0Start, MiKSeg0End;
56 PVOID MmPfnDatabase;
57 ULONG_PTR MmPfnDatabaseEnd;
58 PMEMORY_ALLOCATION_DESCRIPTOR MiFreeDescriptor;
59 MEMORY_ALLOCATION_DESCRIPTOR MiFreeDescriptorOrg;
60 extern KMUTANT MmSystemLoadLock;
61 extern HANDLE MpwThreadHandle;
62 extern BOOLEAN MpwThreadShouldTerminate;
63 extern KEVENT MpwThreadEvent;
64 BOOLEAN MiDbgEnableMdDump =
65 #ifdef _ARM_
66 TRUE;
67 #else
68 FALSE;
69 #endif
70
71 /* PRIVATE FUNCTIONS *********************************************************/
72
73 VOID
74 NTAPI
75 MiShutdownMemoryManager(VOID)
76 {
77 #if 0
78 ULONG PagesWritten;
79 PETHREAD Thread;
80
81 /* Ask MPW thread to shutdown */
82 MpwThreadShouldTerminate = TRUE;
83 KeSetEvent(&MpwThreadEvent, IO_NO_INCREMENT, FALSE);
84
85 /* Wait for it */
86 ObReferenceObjectByHandle(MpwThreadHandle,
87 THREAD_ALL_ACCESS,
88 PsThreadType,
89 KernelMode,
90 (PVOID*)&Thread,
91 NULL);
92
93 KeWaitForSingleObject(Thread,
94 Executive,
95 KernelMode,
96 FALSE,
97 NULL);
98
99 ObDereferenceObject(Thread);
100
101 /* Check if there are any dirty pages, and flush them.
102 There will be no other chance to do this later, since filesystems
103 are going to be shut down. */
104 CcRosFlushDirtyPages(128, &PagesWritten);
105 #endif
106 }
107
108 VOID
109 INIT_FUNCTION
110 NTAPI
111 MmInitVirtualMemory()
112 {
113 PVOID BaseAddress;
114 ULONG Length;
115 NTSTATUS Status;
116 PHYSICAL_ADDRESS BoundaryAddressMultiple;
117 PMEMORY_AREA MArea;
118
119 BoundaryAddressMultiple.QuadPart = 0;
120
121 DPRINT("NonPagedPool %x - %x, PagedPool %x - %x\n", MiNonPagedPoolStart, (ULONG_PTR)MiNonPagedPoolStart + MiNonPagedPoolLength - 1,
122 MmPagedPoolBase, (ULONG_PTR)MmPagedPoolBase + MmPagedPoolSize - 1);
123
124 MiInitializeNonPagedPool();
125
126 /*
127 * Setup the system area descriptor list
128 */
129 MiInitPageDirectoryMap();
130
131 BaseAddress = (PVOID)PCR;
132 MmCreateMemoryArea(MmGetKernelAddressSpace(),
133 MEMORY_AREA_SYSTEM,
134 &BaseAddress,
135 PAGE_SIZE * KeNumberProcessors,
136 PAGE_READWRITE,
137 &MArea,
138 TRUE,
139 0,
140 BoundaryAddressMultiple);
141
142 #if defined(_M_IX86)
143 /* Local APIC base */
144 BaseAddress = (PVOID)0xFEE00000;
145 MmCreateMemoryArea(MmGetKernelAddressSpace(),
146 MEMORY_AREA_SYSTEM,
147 &BaseAddress,
148 PAGE_SIZE,
149 PAGE_READWRITE,
150 &MArea,
151 TRUE,
152 0,
153 BoundaryAddressMultiple);
154
155 /* i/o APIC base */
156 BaseAddress = (PVOID)0xFEC00000;
157 MmCreateMemoryArea(MmGetKernelAddressSpace(),
158 MEMORY_AREA_SYSTEM,
159 &BaseAddress,
160 PAGE_SIZE,
161 PAGE_READWRITE,
162 &MArea,
163 TRUE,
164 0,
165 BoundaryAddressMultiple);
166 #endif
167
168 BaseAddress = MiNonPagedPoolStart;
169 MmCreateMemoryArea(MmGetKernelAddressSpace(),
170 MEMORY_AREA_SYSTEM,
171 &BaseAddress,
172 MiNonPagedPoolLength,
173 PAGE_READWRITE,
174 &MArea,
175 TRUE,
176 0,
177 BoundaryAddressMultiple);
178
179 BaseAddress = MmPagedPoolBase;
180 Status = MmCreateMemoryArea(MmGetKernelAddressSpace(),
181 MEMORY_AREA_PAGED_POOL,
182 &BaseAddress,
183 MmPagedPoolSize,
184 PAGE_READWRITE,
185 &MArea,
186 TRUE,
187 0,
188 BoundaryAddressMultiple);
189
190 MmInitializePagedPool();
191
192 /*
193 * Create the kernel mapping of the user/kernel shared memory.
194 */
195 BaseAddress = (PVOID)KI_USER_SHARED_DATA;
196 Length = PAGE_SIZE;
197 MmCreateMemoryArea(MmGetKernelAddressSpace(),
198 MEMORY_AREA_SYSTEM,
199 &BaseAddress,
200 Length,
201 PAGE_READWRITE,
202 &MArea,
203 TRUE,
204 0,
205 BoundaryAddressMultiple);
206
207 /* Shared data are always located the next page after PCR */
208 MmSharedDataPagePhysicalAddress = MmGetPhysicalAddress((PVOID)PCR);
209 MmSharedDataPagePhysicalAddress.QuadPart += PAGE_SIZE;
210
211 /*
212 *
213 */
214 MmInitializeMemoryConsumer(MC_USER, MmTrimUserMemory);
215 }
216
217 VOID
218 NTAPI
219 MiCountFreePagesInLoaderBlock(PLOADER_PARAMETER_BLOCK LoaderBlock)
220 {
221 PLIST_ENTRY NextEntry;
222 PMEMORY_ALLOCATION_DESCRIPTOR Md;
223 ULONG FreePages = 0;
224
225 for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
226 NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
227 NextEntry = NextEntry->Flink)
228 {
229 Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
230
231 /* Skip invisible memory */
232 if ((Md->MemoryType != LoaderFirmwarePermanent) &&
233 (Md->MemoryType != LoaderSpecialMemory) &&
234 (Md->MemoryType != LoaderHALCachedMemory) &&
235 (Md->MemoryType != LoaderBBTMemory))
236 {
237 /* Check if BURNMEM was used */
238 if (Md->MemoryType != LoaderBad)
239 {
240 /* Count this in the total of pages */
241 MmNumberOfPhysicalPages += Md->PageCount;
242 }
243
244 /* Check if this is the new lowest page */
245 if (Md->BasePage < MmLowestPhysicalPage)
246 {
247 /* Update the lowest page */
248 MmLowestPhysicalPage = Md->BasePage;
249 }
250
251 /* Check if this is the new highest page */
252 if ((Md->BasePage + Md->PageCount) > MmHighestPhysicalPage)
253 {
254 /* Update the highest page */
255 MmHighestPhysicalPage = Md->BasePage + Md->PageCount - 1;
256 }
257
258 /* Check if this is free memory */
259 if ((Md->MemoryType == LoaderFree) ||
260 (Md->MemoryType == LoaderLoadedProgram) ||
261 (Md->MemoryType == LoaderFirmwareTemporary) ||
262 (Md->MemoryType == LoaderOsloaderStack))
263 {
264 /* Check if this is the largest memory descriptor */
265 if (Md->PageCount > FreePages)
266 {
267 /* For now, it is */
268 FreePages = Md->PageCount;
269 MiFreeDescriptor = Md;
270 }
271 }
272 }
273 }
274
275 /* Save original values of the free descriptor, since it'll be
276 altered by early allocations */
277 MiFreeDescriptorOrg = *MiFreeDescriptor;
278 }
279
280 VOID
281 NTAPI
282 MiDbgKernelLayout(VOID)
283 {
284 DPRINT1("%8s%12s\t\t%s\n", "Start", "End", "Type");
285 DPRINT1("0x%p - 0x%p\t%s\n",
286 KSEG0_BASE, MiKSeg0Start,
287 "Undefined region");
288 DPRINT1("0x%p - 0x%p\t%s\n",
289 MiKSeg0Start, MiKSeg0End,
290 "FreeLDR Kernel mapping region");
291 DPRINT1("0x%p - 0x%p\t%s\n",
292 MmPfnDatabase, MmPfnDatabaseEnd,
293 "PFN Database region");
294 if (MmPfnDatabaseEnd != (ULONG_PTR)MiNonPagedPoolStart)
295 DPRINT1("0x%p - 0x%p\t%s\n",
296 MmPfnDatabaseEnd, MiNonPagedPoolStart,
297 "Remaining FreeLDR mapping");
298 DPRINT1("0x%p - 0x%p\t%s\n",
299 MiNonPagedPoolStart, (ULONG_PTR)MiNonPagedPoolStart + MiNonPagedPoolLength,
300 "Non paged pool region");
301 DPRINT1("0x%p - 0x%p\t%s\n",
302 MmPagedPoolBase, (ULONG_PTR)MmPagedPoolBase + MmPagedPoolSize,
303 "Paged pool region");
304 }
305
306 VOID
307 NTAPI
308 MiDbgDumpMemoryDescriptors(VOID)
309 {
310 PLIST_ENTRY NextEntry;
311 PMEMORY_ALLOCATION_DESCRIPTOR Md;
312 ULONG TotalPages = 0;
313
314 DPRINT1("Base\t\tLength\t\tType\n");
315 for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
316 NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
317 NextEntry = NextEntry->Flink)
318 {
319 Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
320 DPRINT1("%08lX\t%08lX\t%s\n", Md->BasePage, Md->PageCount, MemType[Md->MemoryType]);
321 TotalPages += Md->PageCount;
322 }
323
324 DPRINT1("Total: %08lX (%d MB)\n", TotalPages, (TotalPages * PAGE_SIZE) / 1024 / 1024);
325 }
326
327 ULONG_PTR
328 NTAPI
329 MiGetLastKernelAddress(VOID)
330 {
331 PLIST_ENTRY NextEntry;
332 PMEMORY_ALLOCATION_DESCRIPTOR Md;
333 ULONG_PTR LastKrnlPhysAddr = 0;
334
335 for (NextEntry = KeLoaderBlock->MemoryDescriptorListHead.Flink;
336 NextEntry != &KeLoaderBlock->MemoryDescriptorListHead;
337 NextEntry = NextEntry->Flink)
338 {
339 Md = CONTAINING_RECORD(NextEntry, MEMORY_ALLOCATION_DESCRIPTOR, ListEntry);
340
341 if (Md->MemoryType != LoaderFree &&
342 Md->MemoryType != LoaderFirmwareTemporary &&
343 Md->MemoryType != LoaderSpecialMemory)
344 {
345 if (Md->BasePage+Md->PageCount > LastKrnlPhysAddr)
346 LastKrnlPhysAddr = Md->BasePage+Md->PageCount;
347 }
348 }
349
350 /* Convert to a physical address */
351 return LastKrnlPhysAddr << PAGE_SHIFT;
352 }
353
354
355 VOID
356 NTAPI
357 MiInitHyperSpace(VOID);
358
359 VOID
360 INIT_FUNCTION
361 NTAPI
362 MmInit1(VOID)
363 {
364 PLDR_DATA_TABLE_ENTRY LdrEntry;
365 ULONG_PTR Dummy[2];
366
367 /* Dump memory descriptors */
368 if (MiDbgEnableMdDump) MiDbgDumpMemoryDescriptors();
369
370 /* Set the page directory */
371 PsGetCurrentProcess()->Pcb.DirectoryTableBase[0] = (ULONG_PTR)MmGetPageDirectory();
372
373 /* Get the size of FreeLDR's image allocations */
374 MmBootImageSize = KeLoaderBlock->Extension->LoaderPagesSpanned;
375 MmBootImageSize *= PAGE_SIZE;
376
377 /* Set memory limits */
378 MmSystemRangeStart = (PVOID)KSEG0_BASE;
379 MmUserProbeAddress = (ULONG_PTR)MmSystemRangeStart - 0x10000;
380 MmHighestUserAddress = (PVOID)(MmUserProbeAddress - 1);
381 DPRINT("MmSystemRangeStart: %08x\n", MmSystemRangeStart);
382 DPRINT("MmUserProbeAddress: %08x\n", MmUserProbeAddress);
383 DPRINT("MmHighestUserAddress:%08x\n", MmHighestUserAddress);
384
385 /* Initialize memory managment statistics */
386 RtlZeroMemory(&MmStats, sizeof(MmStats));
387
388 /* Count RAM */
389 MiCountFreePagesInLoaderBlock(KeLoaderBlock);
390 DbgPrint("Used memory %dKb\n", (MmNumberOfPhysicalPages * PAGE_SIZE) / 1024);
391
392 /* Initialize the kernel address space */
393 MmInitializeHandBuiltProcess(PsGetCurrentProcess(), Dummy);
394 MmKernelAddressSpace = MmGetCurrentAddressSpace();
395 MmInitGlobalKernelPageDirectory();
396
397 /* Get kernel address boundaries */
398 LdrEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
399 LDR_DATA_TABLE_ENTRY,
400 InLoadOrderLinks);
401 MiKSeg0Start = (ULONG_PTR)LdrEntry->DllBase | KSEG0_BASE;
402 MiKSeg0End = PAGE_ROUND_UP(MiGetLastKernelAddress() | KSEG0_BASE);
403
404 /* We'll put the PFN array right after the loaded modules */
405 MmPfnDatabase = (PVOID)MiKSeg0End;
406 MmPfnDatabaseEnd = (ULONG_PTR)MmPfnDatabase + (MmHighestPhysicalPage * sizeof(PHYSICAL_PAGE));
407 MmPfnDatabaseEnd = PAGE_ROUND_UP(MmPfnDatabaseEnd);
408
409 /*
410 * FreeLDR maps 6MB starting at the kernel base address, followed by the
411 * PFN database. If the PFN database doesn't go over the FreeLDR allocation
412 * then choose the end of the FreeLDR block. If it does go past the FreeLDR
413 * allocation, then choose the next PAGE_SIZE boundary.
414 */
415 if ((ULONG_PTR)MmPfnDatabaseEnd < (MiKSeg0Start + 0x600000))
416 {
417 /* Use the first memory following FreeLDR's 6MB mapping */
418 MiNonPagedPoolStart = (PVOID)((ULONG_PTR)MiKSeg0Start + 0x600000);
419 }
420 else
421 {
422 /* Use the next free available page */
423 MiNonPagedPoolStart = (PVOID)MmPfnDatabaseEnd;
424 }
425
426 /* Length of non-paged pool */
427 MiNonPagedPoolLength = MM_NONPAGED_POOL_SIZE;
428
429 /* Put the paged pool after the non-paged pool */
430 MmPagedPoolBase = (PVOID)PAGE_ROUND_UP((ULONG_PTR)MiNonPagedPoolStart +
431 MiNonPagedPoolLength);
432 MmPagedPoolSize = MM_PAGED_POOL_SIZE;
433
434 /* Dump kernel memory layout */
435 MiDbgKernelLayout();
436
437 /* Initialize hyperspace */
438 MiInitHyperSpace();
439
440 /* Initialize the page list */
441 MmInitializePageList();
442
443 /* Unmap low memory */
444 MmDeletePageTable(NULL, 0);
445
446 /* Intialize memory areas */
447 MmInitVirtualMemory();
448
449 /* Initialize MDLs */
450 MmInitializeMdlImplementation();
451 }
452
453 BOOLEAN
454 NTAPI
455 MmInitSystem(IN ULONG Phase,
456 IN PLOADER_PARAMETER_BLOCK LoaderBlock)
457 {
458 if (Phase == 0)
459 {
460 /* Initialize Mm bootstrap */
461 MmInit1();
462
463 /* Initialize the Loader Lock */
464 KeInitializeMutant(&MmSystemLoadLock, FALSE);
465
466 /* Reload boot drivers */
467 MiReloadBootLoadedDrivers(LoaderBlock);
468
469 /* Initialize the loaded module list */
470 MiInitializeLoadedModuleList(LoaderBlock);
471
472 /* We're done, for now */
473 DPRINT("Mm0: COMPLETE\n");
474 }
475 else if (Phase == 1)
476 {
477 MmInitializeRmapList();
478 MmInitializePageOp();
479 MmInitSectionImplementation();
480 MmInitPagingFile();
481 MmCreatePhysicalMemorySection();
482
483 /* Setup shared user data settings that NT does as well */
484 ASSERT(SharedUserData->NumberOfPhysicalPages == 0);
485 SharedUserData->NumberOfPhysicalPages = MmStats.NrTotalPages;
486 SharedUserData->LargePageMinimum = 0;
487
488 /* For now, we assume that we're always Workstation */
489 SharedUserData->NtProductType = NtProductWinNt;
490 }
491 else if (Phase == 2)
492 {
493 /*
494 * Unmap low memory
495 */
496 MiInitBalancerThread();
497
498 /*
499 * Initialise the modified page writer.
500 */
501 MmInitMpwThread();
502
503 /* Initialize the balance set manager */
504 MmInitBsmThread();
505
506 /* FIXME: Read parameters from memory */
507 }
508
509 return TRUE;
510 }
511
512
513 /* PUBLIC FUNCTIONS **********************************************************/
514
515 /*
516 * @implemented
517 */
518 BOOLEAN
519 NTAPI
520 MmIsThisAnNtAsSystem(VOID)
521 {
522 return IsThisAnNtAsSystem;
523 }
524
525 /*
526 * @implemented
527 */
528 MM_SYSTEMSIZE
529 NTAPI
530 MmQuerySystemSize(VOID)
531 {
532 return MmSystemSize;
533 }