2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/i386/init.c
5 * PURPOSE: ARM Memory Manager Initialization for x86
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
18 /* GLOBALS ********************************************************************/
20 /* Template PTE and PDE for a kernel page */
21 /* FIXME: These should be PTE_GLOBAL */
22 MMPTE ValidKernelPde
= {{PTE_VALID
|PTE_READWRITE
|PTE_DIRTY
|PTE_ACCESSED
}};
23 MMPTE ValidKernelPte
= {{PTE_VALID
|PTE_READWRITE
|PTE_DIRTY
|PTE_ACCESSED
}};
25 /* The same, but for local pages */
26 MMPTE ValidKernelPdeLocal
= {{PTE_VALID
|PTE_READWRITE
|PTE_DIRTY
|PTE_ACCESSED
}};
27 MMPTE ValidKernelPteLocal
= {{PTE_VALID
|PTE_READWRITE
|PTE_DIRTY
|PTE_ACCESSED
}};
29 /* Template PDE for a demand-zero page */
30 MMPDE DemandZeroPde
= {{MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
}};
31 MMPTE DemandZeroPte
= {{MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
}};
33 /* Template PTE for prototype page */
34 MMPTE PrototypePte
= {{(MM_READWRITE
<< MM_PTE_SOFTWARE_PROTECTION_BITS
) |
35 PTE_PROTOTYPE
| (MI_PTE_LOOKUP_NEEDED
<< PAGE_SHIFT
)}};
37 /* Template PTE for decommited page */
38 MMPTE MmDecommittedPte
= {{MM_DECOMMIT
<< MM_PTE_SOFTWARE_PROTECTION_BITS
}};
40 /* PRIVATE FUNCTIONS **********************************************************/
45 MiInitializeSessionSpaceLayout(VOID
)
48 // Set the size of session view, pool, and image
50 MmSessionSize
= MI_SESSION_SIZE
;
51 MmSessionViewSize
= MI_SESSION_VIEW_SIZE
;
52 MmSessionPoolSize
= MI_SESSION_POOL_SIZE
;
53 MmSessionImageSize
= MI_SESSION_IMAGE_SIZE
;
56 // Set the size of system view
58 MmSystemViewSize
= MI_SYSTEM_VIEW_SIZE
;
61 // This is where it all ends
63 MiSessionImageEnd
= (PVOID
)PTE_BASE
;
66 // This is where we will load Win32k.sys and the video driver
68 MiSessionImageStart
= (PVOID
)((ULONG_PTR
)MiSessionImageEnd
-
72 // So the view starts right below the session working set (itself below
75 MiSessionViewStart
= (PVOID
)((ULONG_PTR
)MiSessionImageEnd
-
77 MI_SESSION_WORKING_SET_SIZE
-
81 // Session pool follows
83 MiSessionPoolEnd
= MiSessionViewStart
;
84 MiSessionPoolStart
= (PVOID
)((ULONG_PTR
)MiSessionPoolEnd
-
88 // And it all begins here
90 MmSessionBase
= MiSessionPoolStart
;
93 // Sanity check that our math is correct
95 ASSERT((ULONG_PTR
)MmSessionBase
+ MmSessionSize
== PTE_BASE
);
98 // Session space ends wherever image session space ends
100 MiSessionSpaceEnd
= MiSessionImageEnd
;
103 // System view space ends at session space, so now that we know where
104 // this is, we can compute the base address of system view space itself.
106 MiSystemViewStart
= (PVOID
)((ULONG_PTR
)MmSessionBase
-
109 /* Compute the PTE addresses for all the addresses we carved out */
110 MiSessionImagePteStart
= MiAddressToPte(MiSessionImageStart
);
111 MiSessionImagePteEnd
= MiAddressToPte(MiSessionImageEnd
);
112 MiSessionBasePte
= MiAddressToPte(MmSessionBase
);
113 MiSessionSpaceWs
= (PVOID
)((ULONG_PTR
)MiSessionViewStart
+ MmSessionViewSize
);
114 MiSessionLastPte
= MiAddressToPte(MiSessionSpaceEnd
);
116 /* Initialize session space */
117 MmSessionSpace
= (PMM_SESSION_SPACE
)((ULONG_PTR
)MmSessionBase
+
120 MM_ALLOCATION_GRANULARITY
);
126 MiComputeNonPagedPoolVa(IN ULONG FreePages
)
128 IN PFN_NUMBER PoolPages
;
130 /* Check if this is a machine with less than 256MB of RAM, and no overide */
131 if ((MmNumberOfPhysicalPages
<= MI_MIN_PAGES_FOR_NONPAGED_POOL_TUNING
) &&
132 !(MmSizeOfNonPagedPoolInBytes
))
134 /* Force the non paged pool to be 2MB so we can reduce RAM usage */
135 MmSizeOfNonPagedPoolInBytes
= 2 * _1MB
;
138 /* Hyperspace ends here */
139 MmHyperSpaceEnd
= (PVOID
)((ULONG_PTR
)MmSystemCacheWorkingSetList
- 1);
141 /* Check if the user gave a ridicuously large nonpaged pool RAM size */
142 if ((MmSizeOfNonPagedPoolInBytes
>> PAGE_SHIFT
) > (FreePages
* 7 / 8))
144 /* More than 7/8ths of RAM was dedicated to nonpaged pool, ignore! */
145 MmSizeOfNonPagedPoolInBytes
= 0;
148 /* Check if no registry setting was set, or if the setting was too low */
149 if (MmSizeOfNonPagedPoolInBytes
< MmMinimumNonPagedPoolSize
)
151 /* Start with the minimum (256 KB) and add 32 KB for each MB above 4 */
152 MmSizeOfNonPagedPoolInBytes
= MmMinimumNonPagedPoolSize
;
153 MmSizeOfNonPagedPoolInBytes
+= (FreePages
- 1024) / 256 * MmMinAdditionNonPagedPoolPerMb
;
156 /* Check if the registy setting or our dynamic calculation was too high */
157 if (MmSizeOfNonPagedPoolInBytes
> MI_MAX_INIT_NONPAGED_POOL_SIZE
)
159 /* Set it to the maximum */
160 MmSizeOfNonPagedPoolInBytes
= MI_MAX_INIT_NONPAGED_POOL_SIZE
;
163 /* Check if a percentage cap was set through the registry */
164 if (MmMaximumNonPagedPoolPercent
) UNIMPLEMENTED
;
166 /* Page-align the nonpaged pool size */
167 MmSizeOfNonPagedPoolInBytes
&= ~(PAGE_SIZE
- 1);
169 /* Now, check if there was a registry size for the maximum size */
170 if (!MmMaximumNonPagedPoolInBytes
)
172 /* Start with the default (1MB) */
173 MmMaximumNonPagedPoolInBytes
= MmDefaultMaximumNonPagedPool
;
175 /* Add space for PFN database */
176 MmMaximumNonPagedPoolInBytes
+= (ULONG
)
177 PAGE_ALIGN((MmHighestPhysicalPage
+ 1) * sizeof(MMPFN
));
179 /* Check if the machine has more than 512MB of free RAM */
180 if (FreePages
>= 0x1F000)
182 /* Add 200KB for each MB above 4 */
183 MmMaximumNonPagedPoolInBytes
+= (FreePages
- 1024) / 256 *
184 (MmMaxAdditionNonPagedPoolPerMb
/ 2);
185 if (MmMaximumNonPagedPoolInBytes
< MI_MAX_NONPAGED_POOL_SIZE
)
187 /* Make it at least 128MB since this machine has a lot of RAM */
188 MmMaximumNonPagedPoolInBytes
= MI_MAX_NONPAGED_POOL_SIZE
;
193 /* Add 400KB for each MB above 4 */
194 MmMaximumNonPagedPoolInBytes
+= (FreePages
- 1024) / 256 *
195 MmMaxAdditionNonPagedPoolPerMb
;
199 /* Make sure there's at least 16 pages + the PFN available for expansion */
200 PoolPages
= MmSizeOfNonPagedPoolInBytes
+ (PAGE_SIZE
* 16) +
201 ((ULONG
)PAGE_ALIGN(MmHighestPhysicalPage
+ 1) * sizeof(MMPFN
));
202 if (MmMaximumNonPagedPoolInBytes
< PoolPages
)
204 /* The maximum should be at least high enough to cover all the above */
205 MmMaximumNonPagedPoolInBytes
= PoolPages
;
208 /* Systems with 2GB of kernel address space get double the size */
209 PoolPages
= MI_MAX_NONPAGED_POOL_SIZE
* 2;
211 /* On the other hand, make sure that PFN + nonpaged pool doesn't get too big */
212 if (MmMaximumNonPagedPoolInBytes
> PoolPages
)
214 /* Trim it down to the maximum architectural limit (256MB) */
215 MmMaximumNonPagedPoolInBytes
= PoolPages
;
218 /* Check if this is a system with > 128MB of non paged pool */
219 if (MmMaximumNonPagedPoolInBytes
> MI_MAX_NONPAGED_POOL_SIZE
)
221 /* Check if the initial size is less than the extra 128MB boost */
222 if (MmSizeOfNonPagedPoolInBytes
< (MmMaximumNonPagedPoolInBytes
-
223 MI_MAX_NONPAGED_POOL_SIZE
))
225 /* FIXME: Should check if the initial pool can be expanded */
227 /* Assume no expansion possible, check ift he maximum is too large */
228 if (MmMaximumNonPagedPoolInBytes
> (MmSizeOfNonPagedPoolInBytes
+
229 MI_MAX_NONPAGED_POOL_SIZE
))
231 /* Set it to the initial value plus the boost */
232 MmMaximumNonPagedPoolInBytes
= MmSizeOfNonPagedPoolInBytes
+
233 MI_MAX_NONPAGED_POOL_SIZE
;
242 MiInitMachineDependent(IN PLOADER_PARAMETER_BLOCK LoaderBlock
)
244 PFN_NUMBER PageFrameIndex
;
245 PMMPTE StartPde
, EndPde
, PointerPte
, LastPte
;
246 MMPTE TempPde
, TempPte
;
247 PVOID NonPagedPoolExpansionVa
;
252 #if defined(_GLOBAL_PAGES_ARE_AWESOME_)
254 /* Check for global bit */
255 if (KeFeatureBits
& KF_GLOBAL_PAGE
)
257 /* Set it on the template PTE and PDE */
258 ValidKernelPte
.u
.Hard
.Global
= TRUE
;
259 ValidKernelPde
.u
.Hard
.Global
= TRUE
;
264 /* Now templates are ready */
265 TempPte
= ValidKernelPte
;
266 TempPde
= ValidKernelPde
;
269 // Set CR3 for the system process
271 PointerPte
= MiAddressToPde(PDE_BASE
);
272 PageFrameIndex
= PFN_FROM_PTE(PointerPte
) << PAGE_SHIFT
;
273 PsGetCurrentProcess()->Pcb
.DirectoryTableBase
[0] = PageFrameIndex
;
276 // Blow away user-mode
278 StartPde
= MiAddressToPde(0);
279 EndPde
= MiAddressToPde(KSEG0_BASE
);
280 RtlZeroMemory(StartPde
, (EndPde
- StartPde
) * sizeof(MMPTE
));
282 /* Compute non paged pool limits and size */
283 MiComputeNonPagedPoolVa(MiNumberOfFreePages
);
286 // Now calculate the nonpaged pool expansion VA region
288 MmNonPagedPoolStart
= (PVOID
)((ULONG_PTR
)MmNonPagedPoolEnd
-
289 MmMaximumNonPagedPoolInBytes
+
290 MmSizeOfNonPagedPoolInBytes
);
291 MmNonPagedPoolStart
= (PVOID
)PAGE_ALIGN(MmNonPagedPoolStart
);
292 NonPagedPoolExpansionVa
= MmNonPagedPoolStart
;
293 DPRINT("NP Pool has been tuned to: %lu bytes and %lu bytes\n",
294 MmSizeOfNonPagedPoolInBytes
, MmMaximumNonPagedPoolInBytes
);
297 // Now calculate the nonpaged system VA region, which includes the
298 // nonpaged pool expansion (above) and the system PTEs. Note that it is
299 // then aligned to a PDE boundary (4MB).
301 MiNonPagedSystemSize
= (MmNumberOfSystemPtes
+ 1) * PAGE_SIZE
;
302 MmNonPagedSystemStart
= (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
-
303 MiNonPagedSystemSize
);
304 MmNonPagedSystemStart
= (PVOID
)((ULONG_PTR
)MmNonPagedSystemStart
&
305 ~(PDE_MAPPED_VA
- 1));
308 // Don't let it go below the minimum
310 if (MmNonPagedSystemStart
< (PVOID
)0xEB000000)
313 // This is a hard-coded limit in the Windows NT address space
315 MmNonPagedSystemStart
= (PVOID
)0xEB000000;
318 // Reduce the amount of system PTEs to reach this point
320 MmNumberOfSystemPtes
= ((ULONG_PTR
)MmNonPagedPoolStart
-
321 (ULONG_PTR
)MmNonPagedSystemStart
) >>
323 MmNumberOfSystemPtes
--;
324 ASSERT(MmNumberOfSystemPtes
> 1000);
328 // Check if we are in a situation where the size of the paged pool
329 // is so large that it overflows into nonpaged pool
331 if (MmSizeOfPagedPoolInBytes
>
332 ((ULONG_PTR
)MmNonPagedSystemStart
- (ULONG_PTR
)MmPagedPoolStart
))
335 // We need some recalculations here
337 DPRINT1("Paged pool is too big!\n");
341 // Normally, the PFN database should start after the loader images.
342 // This is already the case in ReactOS, but for now we want to co-exist
343 // with the old memory manager, so we'll create a "Shadow PFN Database"
344 // instead, and arbitrarly start it at 0xB0000000.
346 MmPfnDatabase
= (PVOID
)0xB0000000;
347 ASSERT(((ULONG_PTR
)MmPfnDatabase
& (PDE_MAPPED_VA
- 1)) == 0);
350 // Non paged pool comes after the PFN database
352 MmNonPagedPoolStart
= (PVOID
)((ULONG_PTR
)MmPfnDatabase
+
353 (MxPfnAllocation
<< PAGE_SHIFT
));
356 // Now we actually need to get these many physical pages. Nonpaged pool
357 // is actually also physically contiguous (but not the expansion)
359 PageFrameIndex
= MxGetNextPage(MxPfnAllocation
+
360 (MmSizeOfNonPagedPoolInBytes
>> PAGE_SHIFT
));
361 ASSERT(PageFrameIndex
!= 0);
362 DPRINT("PFN DB PA PFN begins at: %lx\n", PageFrameIndex
);
363 DPRINT("NP PA PFN begins at: %lx\n", PageFrameIndex
+ MxPfnAllocation
);
365 /* Convert nonpaged pool size from bytes to pages */
366 MmMaximumNonPagedPoolInPages
= MmMaximumNonPagedPoolInBytes
>> PAGE_SHIFT
;
369 // Now we need some pages to create the page tables for the NP system VA
370 // which includes system PTEs and expansion NP
372 StartPde
= MiAddressToPde(MmNonPagedSystemStart
);
373 EndPde
= MiAddressToPde((PVOID
)((ULONG_PTR
)MmNonPagedPoolEnd
- 1));
374 while (StartPde
<= EndPde
)
379 TempPde
.u
.Hard
.PageFrameNumber
= MxGetNextPage(1);
380 MI_WRITE_VALID_PTE(StartPde
, TempPde
);
383 // Zero out the page table
385 PointerPte
= MiPteToAddress(StartPde
);
386 RtlZeroMemory(PointerPte
, PAGE_SIZE
);
395 // Now we need pages for the page tables which will map initial NP
397 StartPde
= MiAddressToPde(MmPfnDatabase
);
398 EndPde
= MiAddressToPde((PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
399 MmSizeOfNonPagedPoolInBytes
- 1));
400 while (StartPde
<= EndPde
)
405 TempPde
.u
.Hard
.PageFrameNumber
= MxGetNextPage(1);
406 MI_WRITE_VALID_PTE(StartPde
, TempPde
);
409 // Zero out the page table
411 PointerPte
= MiPteToAddress(StartPde
);
412 RtlZeroMemory(PointerPte
, PAGE_SIZE
);
421 // Now remember where the expansion starts
423 MmNonPagedPoolExpansionStart
= NonPagedPoolExpansionVa
;
426 // Last step is to actually map the nonpaged pool
428 PointerPte
= MiAddressToPte(MmNonPagedPoolStart
);
429 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
430 MmSizeOfNonPagedPoolInBytes
- 1));
431 while (PointerPte
<= LastPte
)
434 // Use one of our contigous pages
436 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
++;
437 MI_WRITE_VALID_PTE(PointerPte
++, TempPte
);
441 // Sanity check: make sure we have properly defined the system PTE space
443 ASSERT(MiAddressToPte(MmNonPagedSystemStart
) <
444 MiAddressToPte(MmNonPagedPoolExpansionStart
));
446 /* Now go ahead and initialize the nonpaged pool */
447 MiInitializeNonPagedPool();
448 MiInitializeNonPagedPoolThresholds();
450 /* Map the PFN database pages */
451 MiMapPfnDatabase(LoaderBlock
);
453 /* Initialize the color tables */
454 MiInitializeColorTables();
456 /* Build the PFN Database */
457 MiInitializePfnDatabase(LoaderBlock
);
458 MmInitializeBalancer(MmAvailablePages
, 0);
461 // Reset the descriptor back so we can create the correct memory blocks
463 *MxFreeDescriptor
= MxOldFreeDescriptor
;
466 // Initialize the nonpaged pool
468 InitializePool(NonPagedPool
, 0);
471 // We PDE-aligned the nonpaged system start VA, so haul some extra PTEs!
473 PointerPte
= MiAddressToPte(MmNonPagedSystemStart
);
474 MmNumberOfSystemPtes
= MiAddressToPte(MmNonPagedPoolExpansionStart
) -
476 MmNumberOfSystemPtes
--;
477 DPRINT("Final System PTE count: %lu (%lu bytes)\n",
478 MmNumberOfSystemPtes
, MmNumberOfSystemPtes
* PAGE_SIZE
);
481 // Create the system PTE space
483 MiInitializeSystemPtes(PointerPte
, MmNumberOfSystemPtes
, SystemPteSpace
);
485 /* Get the PDE For hyperspace */
486 StartPde
= MiAddressToPde(HYPER_SPACE
);
488 /* Lock PFN database */
489 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
491 /* Allocate a page for hyperspace and create it */
492 MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
493 MI_SET_PROCESS2("Kernel");
494 PageFrameIndex
= MiRemoveAnyPage(0);
495 TempPde
= ValidKernelPdeLocal
;
496 TempPde
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
497 MI_WRITE_VALID_PTE(StartPde
, TempPde
);
502 /* Release the lock */
503 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
506 // Zero out the page table now
508 PointerPte
= MiAddressToPte(HYPER_SPACE
);
509 RtlZeroMemory(PointerPte
, PAGE_SIZE
);
512 // Setup the mapping PTEs
514 MmFirstReservedMappingPte
= MiAddressToPte(MI_MAPPING_RANGE_START
);
515 MmLastReservedMappingPte
= MiAddressToPte(MI_MAPPING_RANGE_END
);
516 MmFirstReservedMappingPte
->u
.Hard
.PageFrameNumber
= MI_HYPERSPACE_PTES
;
518 /* Set the working set address */
519 MmWorkingSetList
= (PVOID
)MI_WORKING_SET_LIST
;
522 // Reserve system PTEs for zeroing PTEs and clear them
524 MiFirstReservedZeroingPte
= MiReserveSystemPtes(MI_ZERO_PTES
,
526 RtlZeroMemory(MiFirstReservedZeroingPte
, MI_ZERO_PTES
* sizeof(MMPTE
));
529 // Set the counter to maximum to boot with
531 MiFirstReservedZeroingPte
->u
.Hard
.PageFrameNumber
= MI_ZERO_PTES
- 1;
533 /* Lock PFN database */
534 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
536 /* Reset the ref/share count so that MmInitializeProcessAddressSpace works */
537 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(MiAddressToPde(PDE_BASE
)));
538 Pfn1
->u2
.ShareCount
= 0;
539 Pfn1
->u3
.e2
.ReferenceCount
= 0;
541 /* Get a page for the working set list */
542 MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
543 MI_SET_PROCESS2("Kernel WS List");
544 PageFrameIndex
= MiRemoveAnyPage(0);
545 TempPte
= ValidKernelPteLocal
;
546 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
548 /* Map the working set list */
549 PointerPte
= MiAddressToPte(MmWorkingSetList
);
550 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
552 /* Zero it out, and save the frame index */
553 RtlZeroMemory(MiPteToAddress(PointerPte
), PAGE_SIZE
);
554 PsGetCurrentProcess()->WorkingSetPage
= PageFrameIndex
;
556 /* Check for Pentium LOCK errata */
557 if (KiI386PentiumLockErrataPresent
)
559 /* Mark the 1st IDT page as Write-Through to prevent a lockup
560 on a F00F instruction.
561 See http://www.rcollins.org/Errata/Dec97/F00FBug.html */
562 PointerPte
= MiAddressToPte(KeGetPcr()->IDT
);
563 PointerPte
->u
.Hard
.WriteThrough
= 1;
566 /* Release the lock */
567 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
569 /* Initialize the bogus address space */
571 MmInitializeProcessAddressSpace(PsGetCurrentProcess(), NULL
, NULL
, &Flags
, NULL
);
573 /* Make sure the color lists are valid */
574 ASSERT(MmFreePagesByColor
[0] < (PMMCOLOR_TABLES
)PTE_BASE
);
575 StartPde
= MiAddressToPde(MmFreePagesByColor
[0]);
576 ASSERT(StartPde
->u
.Hard
.Valid
== 1);
577 PointerPte
= MiAddressToPte(MmFreePagesByColor
[0]);
578 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
579 LastPte
= MiAddressToPte((ULONG_PTR
)&MmFreePagesByColor
[1][MmSecondaryColors
] - 1);
580 ASSERT(LastPte
->u
.Hard
.Valid
== 1);
582 /* Loop the color list PTEs */
583 while (PointerPte
<= LastPte
)
585 /* Get the PFN entry */
586 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
587 if (!Pfn1
->u3
.e2
.ReferenceCount
)
590 Pfn1
->u4
.PteFrame
= PFN_FROM_PTE(StartPde
);
591 Pfn1
->PteAddress
= PointerPte
;
592 Pfn1
->u2
.ShareCount
++;
593 Pfn1
->u3
.e2
.ReferenceCount
= 1;
594 Pfn1
->u3
.e1
.PageLocation
= ActiveAndValid
;
595 Pfn1
->u3
.e1
.CacheAttribute
= MiCached
;
603 return STATUS_SUCCESS
;