2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/procsup.c
5 * PURPOSE: ARM Memory Manager Process Related Management
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #define MODULE_INVOLVED_IN_ARM3
16 #include <mm/ARM3/miarm.h>
18 /* GLOBALS ********************************************************************/
20 ULONG MmProcessColorSeed
= 0x12345678;
21 PMMWSL MmWorkingSetList
;
22 ULONG MmMaximumDeadKernelStacks
= 5;
23 SLIST_HEADER MmDeadStackSListHead
;
25 /* PRIVATE FUNCTIONS **********************************************************/
29 MiCreatePebOrTeb(IN PEPROCESS Process
,
31 OUT PULONG_PTR BaseAddress
)
35 ULONG_PTR HighestAddress
, RandomBase
;
37 LARGE_INTEGER CurrentTime
;
40 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'ldaV');
41 if (!Vad
) return STATUS_NO_MEMORY
;
43 /* Setup the primary flags with the size, and make it commited, private, RW */
45 Vad
->u
.VadFlags
.CommitCharge
= BYTES_TO_PAGES(Size
);
46 Vad
->u
.VadFlags
.MemCommit
= TRUE
;
47 Vad
->u
.VadFlags
.PrivateMemory
= TRUE
;
48 Vad
->u
.VadFlags
.Protection
= MM_READWRITE
;
49 Vad
->u
.VadFlags
.NoChange
= TRUE
;
50 Vad
->u1
.Parent
= NULL
;
52 /* Setup the secondary flags to make it a secured, writable, long VAD */
53 Vad
->u2
.LongFlags2
= 0;
54 Vad
->u2
.VadFlags2
.OneSecured
= TRUE
;
55 Vad
->u2
.VadFlags2
.LongVad
= TRUE
;
56 Vad
->u2
.VadFlags2
.ReadOnly
= FALSE
;
58 Vad
->ControlArea
= NULL
; // For Memory-Area hack
59 Vad
->FirstPrototypePte
= NULL
;
61 /* Check if this is a PEB creation */
62 ASSERT(sizeof(TEB
) != sizeof(PEB
));
63 if (Size
== sizeof(PEB
))
65 /* Create a random value to select one page in a 64k region */
66 KeQueryTickCount(&CurrentTime
);
67 CurrentTime
.LowPart
&= (_64K
/ PAGE_SIZE
) - 1;
69 /* Calculate a random base address */
70 RandomBase
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
71 RandomBase
-= CurrentTime
.LowPart
<< PAGE_SHIFT
;
73 /* Make sure the base address is not too high */
74 AlignedSize
= ROUND_TO_PAGES(Size
);
75 if ((RandomBase
+ AlignedSize
) > (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1)
77 RandomBase
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - AlignedSize
;
80 /* Calculate the highest allowed address */
81 HighestAddress
= RandomBase
+ AlignedSize
- 1;
85 HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
89 Status
= MiInsertVadEx((PMMVAD
)Vad
,
95 if (!NT_SUCCESS(Status
))
97 ExFreePoolWithTag(Vad
, 'ldaV');
98 return STATUS_NO_MEMORY
;
102 return STATUS_SUCCESS
;
107 MmDeleteTeb(IN PEPROCESS Process
,
111 PETHREAD Thread
= PsGetCurrentThread();
113 PMM_AVL_TABLE VadTree
= &Process
->VadRoot
;
114 DPRINT("Deleting TEB: %p in %16s\n", Teb
, Process
->ImageFileName
);
116 /* TEB is one page */
117 TebEnd
= (ULONG_PTR
)Teb
+ ROUND_TO_PAGES(sizeof(TEB
)) - 1;
119 /* Attach to the process */
120 KeAttachProcess(&Process
->Pcb
);
122 /* Lock the process address space */
123 KeAcquireGuardedMutex(&Process
->AddressCreationLock
);
125 /* Find the VAD, make sure it's a TEB VAD */
126 Vad
= MiLocateAddress(Teb
);
127 DPRINT("Removing node for VAD: %lx %lx\n", Vad
->StartingVpn
, Vad
->EndingVpn
);
129 if (Vad
->StartingVpn
!= ((ULONG_PTR
)Teb
>> PAGE_SHIFT
))
131 /* Bug in the AVL code? */
132 DPRINT1("Corrupted VAD!\n");
136 /* Sanity checks for a valid TEB VAD */
137 ASSERT((Vad
->StartingVpn
== ((ULONG_PTR
)Teb
>> PAGE_SHIFT
) &&
138 (Vad
->EndingVpn
== (TebEnd
>> PAGE_SHIFT
))));
139 ASSERT(Vad
->u
.VadFlags
.NoChange
== TRUE
);
140 ASSERT(Vad
->u2
.VadFlags2
.OneSecured
== TRUE
);
141 ASSERT(Vad
->u2
.VadFlags2
.MultipleSecured
== FALSE
);
143 /* Lock the working set */
144 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
146 /* Remove this VAD from the tree */
147 ASSERT(VadTree
->NumberGenericTableElements
>= 1);
148 MiRemoveNode((PMMADDRESS_NODE
)Vad
, VadTree
);
150 /* Delete the pages */
151 MiDeleteVirtualAddresses((ULONG_PTR
)Teb
, TebEnd
, NULL
);
153 /* Release the working set */
154 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
160 /* Release the address space lock */
161 KeReleaseGuardedMutex(&Process
->AddressCreationLock
);
169 MmDeleteKernelStack(IN PVOID StackBase
,
173 PFN_NUMBER PageFrameNumber
, PageTableFrameNumber
;
174 PFN_COUNT StackPages
;
180 // This should be the guard page, so decrement by one
182 PointerPte
= MiAddressToPte(StackBase
);
186 // If this is a small stack, just push the stack onto the dead stack S-LIST
190 if (ExQueryDepthSList(&MmDeadStackSListHead
) < MmMaximumDeadKernelStacks
)
192 Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
193 InterlockedPushEntrySList(&MmDeadStackSListHead
,
194 (PSLIST_ENTRY
)&Pfn1
->u1
.NextStackPfn
);
200 // Calculate pages used
202 StackPages
= BYTES_TO_PAGES(GuiStack
?
203 MmLargeStackSize
: KERNEL_STACK_SIZE
);
205 /* Acquire the PFN lock */
206 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
211 for (i
= 0; i
< StackPages
; i
++)
214 // Check if this is a valid PTE
216 if (PointerPte
->u
.Hard
.Valid
== 1)
218 /* Get the PTE's page */
219 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
220 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
222 /* Now get the page of the page table mapping it */
223 PageTableFrameNumber
= Pfn1
->u4
.PteFrame
;
224 Pfn2
= MiGetPfnEntry(PageTableFrameNumber
);
226 /* Remove a shared reference, since the page is going away */
227 MiDecrementShareCount(Pfn2
, PageTableFrameNumber
);
229 /* Set the special pending delete marker */
230 MI_SET_PFN_DELETED(Pfn1
);
232 /* And now delete the actual stack page */
233 MiDecrementShareCount(Pfn1
, PageFrameNumber
);
243 // We should be at the guard page now
245 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
247 /* Release the PFN lock */
248 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
253 MiReleaseSystemPtes(PointerPte
, StackPages
+ 1, SystemPteSpace
);
258 MmCreateKernelStack(IN BOOLEAN GuiStack
,
261 PFN_COUNT StackPtes
, StackPages
;
262 PMMPTE PointerPte
, StackPte
;
264 MMPTE TempPte
, InvalidPte
;
266 PFN_NUMBER PageFrameIndex
;
271 // Calculate pages needed
276 // We'll allocate 64KB stack, but only commit 12K
278 StackPtes
= BYTES_TO_PAGES(MmLargeStackSize
);
279 StackPages
= BYTES_TO_PAGES(KERNEL_LARGE_STACK_COMMIT
);
284 // If the dead stack S-LIST has a stack on it, use it instead of allocating
285 // new system PTEs for this stack
287 if (ExQueryDepthSList(&MmDeadStackSListHead
))
289 Pfn1
= (PMMPFN
)InterlockedPopEntrySList(&MmDeadStackSListHead
);
292 PointerPte
= Pfn1
->PteAddress
;
293 BaseAddress
= MiPteToAddress(++PointerPte
);
299 // We'll allocate 12K and that's it
301 StackPtes
= BYTES_TO_PAGES(KERNEL_STACK_SIZE
);
302 StackPages
= StackPtes
;
306 // Reserve stack pages, plus a guard page
308 StackPte
= MiReserveSystemPtes(StackPtes
+ 1, SystemPteSpace
);
309 if (!StackPte
) return NULL
;
312 // Get the stack address
314 BaseAddress
= MiPteToAddress(StackPte
+ StackPtes
+ 1);
317 // Select the right PTE address where we actually start committing pages
319 PointerPte
= StackPte
;
320 if (GuiStack
) PointerPte
+= BYTES_TO_PAGES(MmLargeStackSize
-
321 KERNEL_LARGE_STACK_COMMIT
);
324 /* Setup the temporary invalid PTE */
325 MI_MAKE_SOFTWARE_PTE(&InvalidPte
, MM_NOACCESS
);
327 /* Setup the template stack PTE */
328 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte
, PointerPte
+ 1, MM_READWRITE
, 0);
331 // Acquire the PFN DB lock
333 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
336 // Loop each stack page
338 for (i
= 0; i
< StackPages
; i
++)
345 /* Get a page and write the current invalid PTE */
346 MI_SET_USAGE(MI_USAGE_KERNEL_STACK
);
347 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName
);
348 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
349 MI_WRITE_INVALID_PTE(PointerPte
, InvalidPte
);
351 /* Initialize the PFN entry for this page */
352 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
354 /* Write the valid PTE */
355 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
356 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
360 // Release the PFN lock
362 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
365 // Return the stack address
372 MmGrowKernelStackEx(IN PVOID StackPointer
,
375 PKTHREAD Thread
= KeGetCurrentThread();
376 PMMPTE LimitPte
, NewLimitPte
, LastPte
;
378 MMPTE TempPte
, InvalidPte
;
379 PFN_NUMBER PageFrameIndex
;
382 // Make sure the stack did not overflow
384 ASSERT(((ULONG_PTR
)Thread
->StackBase
- (ULONG_PTR
)Thread
->StackLimit
) <=
385 (MmLargeStackSize
+ PAGE_SIZE
));
388 // Get the current stack limit
390 LimitPte
= MiAddressToPte(Thread
->StackLimit
);
391 ASSERT(LimitPte
->u
.Hard
.Valid
== 1);
394 // Get the new one and make sure this isn't a retarded request
396 NewLimitPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)StackPointer
- GrowSize
));
397 if (NewLimitPte
== LimitPte
) return STATUS_SUCCESS
;
400 // Now make sure you're not going past the reserved space
402 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)Thread
->StackBase
-
404 if (NewLimitPte
< LastPte
)
409 DPRINT1("Thread wants too much stack\n");
410 return STATUS_STACK_OVERFLOW
;
414 // Calculate the number of new pages
418 /* Setup the temporary invalid PTE */
419 MI_MAKE_SOFTWARE_PTE(&InvalidPte
, MM_NOACCESS
);
422 // Acquire the PFN DB lock
424 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
427 // Loop each stack page
429 while (LimitPte
>= NewLimitPte
)
431 /* Get a page and write the current invalid PTE */
432 MI_SET_USAGE(MI_USAGE_KERNEL_STACK_EXPANSION
);
433 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName
);
434 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
435 MI_WRITE_INVALID_PTE(LimitPte
, InvalidPte
);
437 /* Initialize the PFN entry for this page */
438 MiInitializePfn(PageFrameIndex
, LimitPte
, 1);
440 /* Setup the template stack PTE */
441 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte
, LimitPte
, MM_READWRITE
, PageFrameIndex
);
443 /* Write the valid PTE */
444 MI_WRITE_VALID_PTE(LimitPte
--, TempPte
);
448 // Release the PFN lock
450 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
455 Thread
->StackLimit
= (ULONG_PTR
)MiPteToAddress(NewLimitPte
);
456 return STATUS_SUCCESS
;
461 MmGrowKernelStack(IN PVOID StackPointer
)
464 // Call the extended version
466 return MmGrowKernelStackEx(StackPointer
, KERNEL_LARGE_STACK_COMMIT
);
471 MmSetMemoryPriorityProcess(IN PEPROCESS Process
,
472 IN UCHAR MemoryPriority
)
477 // Check if we have less then 16MB of Physical Memory
479 if ((MmSystemSize
== MmSmallSystem
) &&
480 (MmNumberOfPhysicalPages
< ((15 * 1024 * 1024) / PAGE_SIZE
)))
483 // Always use background priority
485 MemoryPriority
= MEMORY_PRIORITY_BACKGROUND
;
489 // Save the old priority and update it
491 OldPriority
= (UCHAR
)Process
->Vm
.Flags
.MemoryPriority
;
492 Process
->Vm
.Flags
.MemoryPriority
= MemoryPriority
;
495 // Return the old priority
502 MmCreatePeb(IN PEPROCESS Process
,
503 IN PINITIAL_PEB InitialPeb
,
507 LARGE_INTEGER SectionOffset
;
509 PVOID TableBase
= NULL
;
510 PIMAGE_NT_HEADERS NtHeaders
;
511 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData
;
513 USHORT Characteristics
;
514 KAFFINITY ProcessAffinityMask
= 0;
515 SectionOffset
.QuadPart
= (ULONGLONG
)0;
521 KeAttachProcess(&Process
->Pcb
);
526 Status
= MmMapViewOfSection(ExpNlsSectionPointer
,
536 DPRINT("NLS Tables at: %p\n", TableBase
);
537 if (!NT_SUCCESS(Status
))
539 /* Cleanup and exit */
547 Status
= MiCreatePebOrTeb(Process
, sizeof(PEB
), (PULONG_PTR
)&Peb
);
548 DPRINT("PEB at: %p\n", Peb
);
549 if (!NT_SUCCESS(Status
))
551 /* Cleanup and exit */
557 // Use SEH in case we can't load the PEB
562 // Initialize the PEB
564 RtlZeroMemory(Peb
, sizeof(PEB
));
569 Peb
->ImageBaseAddress
= Process
->SectionBaseAddress
;
570 Peb
->InheritedAddressSpace
= InitialPeb
->InheritedAddressSpace
;
571 Peb
->Mutant
= InitialPeb
->Mutant
;
572 Peb
->ImageUsesLargePages
= InitialPeb
->ImageUsesLargePages
;
577 Peb
->AnsiCodePageData
= (PCHAR
)TableBase
+ ExpAnsiCodePageDataOffset
;
578 Peb
->OemCodePageData
= (PCHAR
)TableBase
+ ExpOemCodePageDataOffset
;
579 Peb
->UnicodeCaseTableData
= (PCHAR
)TableBase
+ ExpUnicodeCaseTableDataOffset
;
582 // Default Version Data (could get changed below)
584 Peb
->OSMajorVersion
= NtMajorVersion
;
585 Peb
->OSMinorVersion
= NtMinorVersion
;
586 Peb
->OSBuildNumber
= (USHORT
)(NtBuildNumber
& 0x3FFF);
587 Peb
->OSPlatformId
= VER_PLATFORM_WIN32_NT
;
588 Peb
->OSCSDVersion
= (USHORT
)CmNtCSDVersion
;
591 // Heap and Debug Data
593 Peb
->NumberOfProcessors
= KeNumberProcessors
;
594 Peb
->BeingDebugged
= (BOOLEAN
)(Process
->DebugPort
!= NULL
);
595 Peb
->NtGlobalFlag
= NtGlobalFlag
;
596 Peb
->HeapSegmentReserve
= MmHeapSegmentReserve
;
597 Peb
->HeapSegmentCommit
= MmHeapSegmentCommit
;
598 Peb
->HeapDeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
599 Peb
->HeapDeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
600 Peb
->CriticalSectionTimeout
= MmCriticalSectionTimeout
;
601 Peb
->MinimumStackCommit
= MmMinimumStackCommitInBytes
;
602 Peb
->MaximumNumberOfHeaps
= (PAGE_SIZE
- sizeof(PEB
)) / sizeof(PVOID
);
603 Peb
->ProcessHeaps
= (PVOID
*)(Peb
+ 1);
608 if (Process
->Session
) Peb
->SessionId
= MmGetSessionId(Process
);
610 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
616 _SEH2_YIELD(return _SEH2_GetExceptionCode());
621 // Use SEH in case we can't load the image
628 NtHeaders
= RtlImageNtHeader(Peb
->ImageBaseAddress
);
629 Characteristics
= NtHeaders
->FileHeader
.Characteristics
;
631 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
637 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT
);
647 // Use SEH in case we can't load the headers
652 // Get the Image Config Data too
654 ImageConfigData
= RtlImageDirectoryEntryToData(Peb
->ImageBaseAddress
,
656 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
,
663 ProbeForRead(ImageConfigData
,
664 sizeof(IMAGE_LOAD_CONFIG_DIRECTORY
),
669 // Write subsystem data
671 Peb
->ImageSubsystem
= NtHeaders
->OptionalHeader
.Subsystem
;
672 Peb
->ImageSubsystemMajorVersion
= NtHeaders
->OptionalHeader
.MajorSubsystemVersion
;
673 Peb
->ImageSubsystemMinorVersion
= NtHeaders
->OptionalHeader
.MinorSubsystemVersion
;
676 // Check for version data
678 if (NtHeaders
->OptionalHeader
.Win32VersionValue
)
681 // Extract values and write them
683 Peb
->OSMajorVersion
= NtHeaders
->OptionalHeader
.Win32VersionValue
& 0xFF;
684 Peb
->OSMinorVersion
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 8) & 0xFF;
685 Peb
->OSBuildNumber
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 16) & 0x3FFF;
686 Peb
->OSPlatformId
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 30) ^ 2;
688 /* Process CSD version override */
689 if ((ImageConfigData
) && (ImageConfigData
->CSDVersion
))
691 /* Take the value from the image configuration directory */
692 Peb
->OSCSDVersion
= ImageConfigData
->CSDVersion
;
696 /* Process optional process affinity mask override */
697 if ((ImageConfigData
) && (ImageConfigData
->ProcessAffinityMask
))
699 /* Take the value from the image configuration directory */
700 ProcessAffinityMask
= ImageConfigData
->ProcessAffinityMask
;
704 // Check if this is a UP image
705 if (Characteristics
& IMAGE_FILE_UP_SYSTEM_ONLY
)
708 // Force it to use CPU 0
710 /* FIXME: this should use the MmRotatingUniprocessorNumber */
711 Peb
->ImageProcessAffinityMask
= 0;
716 // Whatever was configured
718 Peb
->ImageProcessAffinityMask
= ProcessAffinityMask
;
721 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
727 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT
);
733 // Detach from the Process
737 return STATUS_SUCCESS
;
742 MmCreateTeb(IN PEPROCESS Process
,
743 IN PCLIENT_ID ClientId
,
744 IN PINITIAL_TEB InitialTeb
,
748 NTSTATUS Status
= STATUS_SUCCESS
;
754 KeAttachProcess(&Process
->Pcb
);
759 Status
= MiCreatePebOrTeb(Process
, sizeof(TEB
), (PULONG_PTR
)&Teb
);
760 ASSERT(NT_SUCCESS(Status
));
763 // Use SEH in case we can't load the TEB
768 // Initialize the PEB
770 RtlZeroMemory(Teb
, sizeof(TEB
));
776 Teb
->NtTib
.ExceptionList
= NULL
;
778 Teb
->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
780 Teb
->NtTib
.Self
= (PNT_TIB
)Teb
;
783 // Identify this as an OS/2 V3.0 ("Cruiser") TIB
785 Teb
->NtTib
.Version
= 30 << 8;
790 Teb
->ClientId
= *ClientId
;
791 Teb
->RealClientId
= *ClientId
;
792 Teb
->ProcessEnvironmentBlock
= Process
->Peb
;
793 Teb
->CurrentLocale
= PsDefaultThreadLocaleId
;
796 // Check if we have a grandparent TEB
798 if ((InitialTeb
->PreviousStackBase
== NULL
) &&
799 (InitialTeb
->PreviousStackLimit
== NULL
))
802 // Use initial TEB values
804 Teb
->NtTib
.StackBase
= InitialTeb
->StackBase
;
805 Teb
->NtTib
.StackLimit
= InitialTeb
->StackLimit
;
806 Teb
->DeallocationStack
= InitialTeb
->AllocatedStackBase
;
811 // Use grandparent TEB values
813 Teb
->NtTib
.StackBase
= InitialTeb
->PreviousStackBase
;
814 Teb
->NtTib
.StackLimit
= InitialTeb
->PreviousStackLimit
;
818 // Initialize the static unicode string
820 Teb
->StaticUnicodeString
.MaximumLength
= sizeof(Teb
->StaticUnicodeBuffer
);
821 Teb
->StaticUnicodeString
.Buffer
= Teb
->StaticUnicodeBuffer
;
823 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
828 Status
= _SEH2_GetExceptionCode();
842 MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess
)
848 /* Setup some bogus list data */
849 MmWorkingSetList
->LastEntry
= CurrentProcess
->Vm
.MinimumWorkingSetSize
;
850 MmWorkingSetList
->HashTable
= NULL
;
851 MmWorkingSetList
->HashTableSize
= 0;
852 MmWorkingSetList
->NumberOfImageWaiters
= 0;
853 MmWorkingSetList
->Wsle
= (PVOID
)0xDEADBABE;
854 MmWorkingSetList
->VadBitMapHint
= 1;
855 MmWorkingSetList
->HashTableStart
= (PVOID
)0xBADAB00B;
856 MmWorkingSetList
->HighestPermittedHashAddress
= (PVOID
)0xCAFEBABE;
857 MmWorkingSetList
->FirstFree
= 1;
858 MmWorkingSetList
->FirstDynamic
= 2;
859 MmWorkingSetList
->NextSlot
= 3;
860 MmWorkingSetList
->LastInitializedWsle
= 4;
862 /* The rule is that the owner process is always in the FLINK of the PDE's PFN entry */
863 Pfn1
= MiGetPfnEntry(CurrentProcess
->Pcb
.DirectoryTableBase
[0] >> PAGE_SHIFT
);
864 ASSERT(Pfn1
->u4
.PteFrame
== MiGetPfnEntryIndex(Pfn1
));
865 Pfn1
->u1
.Event
= (PKEVENT
)CurrentProcess
;
867 /* Map the process working set in kernel space */
868 sysPte
= MiReserveSystemPtes(1, SystemPteSpace
);
869 MI_MAKE_HARDWARE_PTE_KERNEL(&tempPte
, sysPte
, MM_READWRITE
, CurrentProcess
->WorkingSetPage
);
870 MI_WRITE_VALID_PTE(sysPte
, tempPte
);
871 CurrentProcess
->Vm
.VmWorkingSetList
= MiPteToAddress(sysPte
);
876 MmInitializeProcessAddressSpace(IN PEPROCESS Process
,
877 IN PEPROCESS ProcessClone OPTIONAL
,
878 IN PVOID Section OPTIONAL
,
880 IN POBJECT_NAME_INFORMATION
*AuditName OPTIONAL
)
882 NTSTATUS Status
= STATUS_SUCCESS
;
885 PROS_SECTION_OBJECT SectionObject
= Section
;
889 PFN_NUMBER PageFrameNumber
;
890 UNICODE_STRING FileName
;
896 /* We should have a PDE */
897 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] != 0);
898 ASSERT(Process
->PdeUpdateNeeded
== FALSE
);
900 /* Attach to the process */
901 KeAttachProcess(&Process
->Pcb
);
903 /* The address space should now been in phase 1 or 0 */
904 ASSERT(Process
->AddressSpaceInitialized
<= 1);
905 Process
->AddressSpaceInitialized
= 2;
907 /* Initialize the Addresss Space lock */
908 KeInitializeGuardedMutex(&Process
->AddressCreationLock
);
909 Process
->Vm
.WorkingSetExpansionLinks
.Flink
= NULL
;
911 /* Initialize AVL tree */
912 ASSERT(Process
->VadRoot
.NumberGenericTableElements
== 0);
913 Process
->VadRoot
.BalancedRoot
.u1
.Parent
= &Process
->VadRoot
.BalancedRoot
;
915 /* Lock PFN database */
916 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
918 /* Setup the PFN for the PDE base of this process */
920 PointerPte
= MiAddressToPte(PXE_BASE
);
922 PointerPte
= MiAddressToPte(PDE_BASE
);
924 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
925 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] == PageFrameNumber
* PAGE_SIZE
);
926 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
928 /* Do the same for hyperspace */
930 PointerPde
= MiAddressToPxe((PVOID
)HYPER_SPACE
);
932 PointerPde
= MiAddressToPde(HYPER_SPACE
);
934 PageFrameNumber
= PFN_FROM_PTE(PointerPde
);
935 //ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE); // we're not lucky
936 MiInitializePfn(PageFrameNumber
, (PMMPTE
)PointerPde
, TRUE
);
938 /* Setup the PFN for the PTE for the working set */
939 PointerPte
= MiAddressToPte(MI_WORKING_SET_LIST
);
940 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, MM_READWRITE
, 0);
941 ASSERT(PointerPte
->u
.Long
!= 0);
942 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
943 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPte
);
944 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
945 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
946 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
948 /* Now initialize the working set list */
949 MiInitializeWorkingSetList(Process
);
952 ASSERT(Process
->PhysicalVadRoot
== NULL
);
954 /* Release PFN lock */
955 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
957 /* Check if there's a Section Object */
960 /* Determine the image file name and save it to EPROCESS */
961 FileName
= SectionObject
->FileObject
->FileName
;
962 Source
= (PWCHAR
)((PCHAR
)FileName
.Buffer
+ FileName
.Length
);
965 /* Loop the file name*/
966 while (Source
> FileName
.Buffer
)
968 /* Make sure this isn't a backslash */
969 if (*--Source
== OBJ_NAME_PATH_SEPARATOR
)
971 /* If so, stop it here */
977 /* Otherwise, keep going */
983 /* Copy the to the process and truncate it to 15 characters if necessary */
984 Destination
= Process
->ImageFileName
;
985 Length
= min(Length
, sizeof(Process
->ImageFileName
) - 1);
986 while (Length
--) *Destination
++ = (UCHAR
)*Source
++;
987 *Destination
= ANSI_NULL
;
989 /* Check if caller wants an audit name */
992 /* Setup the audit name */
993 Status
= SeInitializeProcessAuditName(SectionObject
->FileObject
,
996 if (!NT_SUCCESS(Status
))
1004 /* Map the section */
1005 Status
= MmMapViewOfSection(Section
,
1016 /* Save the pointer */
1017 Process
->SectionBaseAddress
= ImageBase
;
1020 /* Be nice and detach */
1023 /* Return status to caller */
1030 MmInitializeHandBuiltProcess(IN PEPROCESS Process
,
1031 IN PULONG_PTR DirectoryTableBase
)
1033 /* Share the directory base with the idle process */
1034 DirectoryTableBase
[0] = PsGetCurrentProcess()->Pcb
.DirectoryTableBase
[0];
1035 DirectoryTableBase
[1] = PsGetCurrentProcess()->Pcb
.DirectoryTableBase
[1];
1037 /* Initialize the Addresss Space */
1038 KeInitializeGuardedMutex(&Process
->AddressCreationLock
);
1039 KeInitializeSpinLock(&Process
->HyperSpaceLock
);
1040 Process
->Vm
.WorkingSetExpansionLinks
.Flink
= NULL
;
1041 ASSERT(Process
->VadRoot
.NumberGenericTableElements
== 0);
1042 Process
->VadRoot
.BalancedRoot
.u1
.Parent
= &Process
->VadRoot
.BalancedRoot
;
1044 /* Use idle process Working set */
1045 Process
->Vm
.VmWorkingSetList
= PsGetCurrentProcess()->Vm
.VmWorkingSetList
;
1048 Process
->HasAddressSpace
= TRUE
;//??
1049 return STATUS_SUCCESS
;
1055 MmInitializeHandBuiltProcess2(IN PEPROCESS Process
)
1057 /* Lock the VAD, ARM3-owned ranges away */
1058 return STATUS_SUCCESS
;
1062 /* FIXME: Evaluate ways to make this portable yet arch-specific */
1065 MmCreateProcessAddressSpace(IN ULONG MinWs
,
1066 IN PEPROCESS Process
,
1067 OUT PULONG_PTR DirectoryTableBase
)
1070 PFN_NUMBER PdeIndex
, HyperIndex
, WsListIndex
;
1072 MMPTE TempPte
, PdePte
;
1074 PMMPTE SystemTable
, HyperTable
;
1078 /* Choose a process color */
1079 Process
->NextPageColor
= (USHORT
)RtlRandom(&MmProcessColorSeed
);
1081 /* Setup the hyperspace lock */
1082 KeInitializeSpinLock(&Process
->HyperSpaceLock
);
1084 /* Lock PFN database */
1085 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1087 /* Get a zero page for the PDE, if possible */
1088 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1089 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY
);
1090 PdeIndex
= MiRemoveZeroPageSafe(Color
);
1093 /* No zero pages, grab a free one */
1094 PdeIndex
= MiRemoveAnyPage(Color
);
1096 /* Zero it outside the PFN lock */
1097 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1098 MiZeroPhysicalPage(PdeIndex
);
1099 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1102 /* Get a zero page for hyperspace, if possible */
1103 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY
);
1104 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1105 HyperIndex
= MiRemoveZeroPageSafe(Color
);
1108 /* No zero pages, grab a free one */
1109 HyperIndex
= MiRemoveAnyPage(Color
);
1111 /* Zero it outside the PFN lock */
1112 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1113 MiZeroPhysicalPage(HyperIndex
);
1114 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1117 /* Get a zero page for the woring set list, if possible */
1118 MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
1119 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1120 WsListIndex
= MiRemoveZeroPageSafe(Color
);
1123 /* No zero pages, grab a free one */
1124 WsListIndex
= MiRemoveAnyPage(Color
);
1126 /* Zero it outside the PFN lock */
1127 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1128 MiZeroPhysicalPage(WsListIndex
);
1132 /* Release the PFN lock */
1133 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1136 /* Switch to phase 1 initialization */
1137 ASSERT(Process
->AddressSpaceInitialized
== 0);
1138 Process
->AddressSpaceInitialized
= 1;
1140 /* Set the base directory pointers */
1141 Process
->WorkingSetPage
= WsListIndex
;
1142 DirectoryTableBase
[0] = PdeIndex
<< PAGE_SHIFT
;
1143 DirectoryTableBase
[1] = HyperIndex
<< PAGE_SHIFT
;
1145 /* Make sure we don't already have a page directory setup */
1146 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] == 0);
1148 /* Get a PTE to map hyperspace */
1149 PointerPte
= MiReserveSystemPtes(1, SystemPteSpace
);
1150 ASSERT(PointerPte
!= NULL
);
1153 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte
,
1158 /* Set it dirty and map it */
1159 MI_MAKE_DIRTY_PAGE(&PdePte
);
1160 MI_WRITE_VALID_PTE(PointerPte
, PdePte
);
1162 /* Now get hyperspace's page table */
1163 HyperTable
= MiPteToAddress(PointerPte
);
1165 /* Now write the PTE/PDE entry for the working set list index itself */
1166 TempPte
= ValidKernelPte
;
1167 TempPte
.u
.Hard
.PageFrameNumber
= WsListIndex
;
1168 /* Hyperspace is local */
1169 MI_MAKE_LOCAL_PAGE(&TempPte
);
1170 PdeOffset
= MiAddressToPteOffset(MmWorkingSetList
);
1171 HyperTable
[PdeOffset
] = TempPte
;
1173 /* Let go of the system PTE */
1174 MiReleaseSystemPtes(PointerPte
, 1, SystemPteSpace
);
1176 /* Save the PTE address of the page directory itself */
1177 Pfn1
= MiGetPfnEntry(PdeIndex
);
1178 Pfn1
->PteAddress
= (PMMPTE
)PDE_BASE
;
1180 /* Insert us into the Mm process list */
1181 OldIrql
= MiAcquireExpansionLock();
1182 InsertTailList(&MmProcessList
, &Process
->MmProcessLinks
);
1183 MiReleaseExpansionLock(OldIrql
);
1185 /* Get a PTE to map the page directory */
1186 PointerPte
= MiReserveSystemPtes(1, SystemPteSpace
);
1187 ASSERT(PointerPte
!= NULL
);
1190 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte
,
1195 /* Set it dirty and map it */
1196 MI_MAKE_DIRTY_PAGE(&PdePte
);
1197 MI_WRITE_VALID_PTE(PointerPte
, PdePte
);
1199 /* Now get the page directory (which we'll double map, so call it a page table */
1200 SystemTable
= MiPteToAddress(PointerPte
);
1202 /* Copy all the kernel mappings */
1203 PdeOffset
= MiGetPdeOffset(MmSystemRangeStart
);
1204 RtlCopyMemory(&SystemTable
[PdeOffset
],
1205 MiAddressToPde(MmSystemRangeStart
),
1206 PAGE_SIZE
- PdeOffset
* sizeof(MMPTE
));
1208 /* Now write the PTE/PDE entry for hyperspace itself */
1209 TempPte
= ValidKernelPte
;
1210 TempPte
.u
.Hard
.PageFrameNumber
= HyperIndex
;
1211 PdeOffset
= MiGetPdeOffset(HYPER_SPACE
);
1212 SystemTable
[PdeOffset
] = TempPte
;
1216 ASSERT(MiGetPdeOffset(MmHyperSpaceEnd
) >= PdeOffset
);
1218 /* Now do the x86 trick of making the PDE a page table itself */
1219 PdeOffset
= MiGetPdeOffset(PTE_BASE
);
1220 TempPte
.u
.Hard
.PageFrameNumber
= PdeIndex
;
1221 SystemTable
[PdeOffset
] = TempPte
;
1223 /* Let go of the system PTE */
1224 MiReleaseSystemPtes(PointerPte
, 1, SystemPteSpace
);
1226 /* Add the process to the session */
1227 MiSessionAddProcess(Process
);
1234 MmCleanProcessAddressSpace(IN PEPROCESS Process
)
1237 PMM_AVL_TABLE VadTree
;
1238 PETHREAD Thread
= PsGetCurrentThread();
1240 /* Only support this */
1241 ASSERT(Process
->AddressSpaceInitialized
== 2);
1243 /* Remove from the session */
1244 MiSessionRemoveProcess();
1246 /* Lock the process address space from changes */
1247 MmLockAddressSpace(&Process
->Vm
);
1248 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1250 /* VM is deleted now */
1251 Process
->VmDeleted
= TRUE
;
1252 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1254 /* Enumerate the VADs */
1255 VadTree
= &Process
->VadRoot
;
1256 while (VadTree
->NumberGenericTableElements
)
1258 /* Grab the current VAD */
1259 Vad
= (PMMVAD
)VadTree
->BalancedRoot
.RightChild
;
1261 /* Check for old-style memory areas */
1262 if (Vad
->u
.VadFlags
.Spare
== 1)
1264 /* Let RosMm handle this */
1265 MiRosCleanupMemoryArea(Process
, Vad
);
1269 /* Lock the working set */
1270 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1272 /* Remove this VAD from the tree */
1273 ASSERT(VadTree
->NumberGenericTableElements
>= 1);
1274 MiRemoveNode((PMMADDRESS_NODE
)Vad
, VadTree
);
1276 /* Only regular VADs supported for now */
1277 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1279 /* Check if this is a section VAD */
1280 if (!(Vad
->u
.VadFlags
.PrivateMemory
) && (Vad
->ControlArea
))
1282 /* Remove the view */
1283 MiRemoveMappedView(Process
, Vad
);
1287 /* Delete the addresses */
1288 MiDeleteVirtualAddresses(Vad
->StartingVpn
<< PAGE_SHIFT
,
1289 (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1),
1292 /* Release the working set */
1293 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1296 /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */
1297 if (Vad
->u
.VadFlags
.Spare
== 1)
1299 /* Set a flag so MmDeleteMemoryArea knows to free, but not to remove */
1300 Vad
->u
.VadFlags
.Spare
= 2;
1304 /* Free the VAD memory */
1308 /* Lock the working set */
1309 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1310 ASSERT(Process
->CloneRoot
== NULL
);
1311 ASSERT(Process
->PhysicalVadRoot
== NULL
);
1313 /* Delete the shared user data section */
1314 MiDeleteVirtualAddresses(USER_SHARED_DATA
, USER_SHARED_DATA
, NULL
);
1316 /* Release the working set */
1317 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1319 /* Release the address space */
1320 MmUnlockAddressSpace(&Process
->Vm
);
1325 MmDeleteProcessAddressSpace2(IN PEPROCESS Process
)
1329 PFN_NUMBER PageFrameIndex
;
1331 //ASSERT(Process->CommitCharge == 0);
1333 /* Acquire the PFN lock */
1334 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1336 /* Check for fully initialized process */
1337 if (Process
->AddressSpaceInitialized
== 2)
1339 /* Map the working set page and its page table */
1340 Pfn1
= MiGetPfnEntry(Process
->WorkingSetPage
);
1341 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1344 MI_SET_PFN_DELETED(Pfn1
);
1345 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1346 MiDecrementShareCount(Pfn1
, Process
->WorkingSetPage
);
1347 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1348 MiReleaseSystemPtes(MiAddressToPte(Process
->Vm
.VmWorkingSetList
), 1, SystemPteSpace
);
1350 /* Now map hyperspace and its page table */
1351 PageFrameIndex
= Process
->Pcb
.DirectoryTableBase
[1] >> PAGE_SHIFT
;
1352 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1353 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1356 MI_SET_PFN_DELETED(Pfn1
);
1357 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1358 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1359 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1361 /* Finally, nuke the PDE itself */
1362 PageFrameIndex
= Process
->Pcb
.DirectoryTableBase
[0] >> PAGE_SHIFT
;
1363 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1364 MI_SET_PFN_DELETED(Pfn1
);
1365 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1366 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1368 /* Page table is now dead. Bye bye... */
1369 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1373 /* A partly-initialized process should never exit through here */
1377 /* Release the PFN lock */
1378 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1380 /* Drop a reference on the session */
1381 if (Process
->Session
) MiReleaseProcessReferenceToSessionDataPage(Process
->Session
);
1383 /* Clear out the PDE pages */
1384 Process
->Pcb
.DirectoryTableBase
[0] = 0;
1385 Process
->Pcb
.DirectoryTableBase
[1] = 0;
1389 /* SYSTEM CALLS ***************************************************************/
1393 NtAllocateUserPhysicalPages(IN HANDLE ProcessHandle
,
1394 IN OUT PULONG_PTR NumberOfPages
,
1395 IN OUT PULONG_PTR UserPfnArray
)
1398 return STATUS_NOT_IMPLEMENTED
;
1403 NtMapUserPhysicalPages(IN PVOID VirtualAddresses
,
1404 IN ULONG_PTR NumberOfPages
,
1405 IN OUT PULONG_PTR UserPfnArray
)
1408 return STATUS_NOT_IMPLEMENTED
;
1413 NtMapUserPhysicalPagesScatter(IN PVOID
*VirtualAddresses
,
1414 IN ULONG_PTR NumberOfPages
,
1415 IN OUT PULONG_PTR UserPfnArray
)
1418 return STATUS_NOT_IMPLEMENTED
;
1423 NtFreeUserPhysicalPages(IN HANDLE ProcessHandle
,
1424 IN OUT PULONG_PTR NumberOfPages
,
1425 IN OUT PULONG_PTR UserPfnArray
)
1428 return STATUS_NOT_IMPLEMENTED
;