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 "../ARM3/miarm.h"
18 /* GLOBALS ********************************************************************/
20 ULONG MmProcessColorSeed
= 0x12345678;
21 PMMWSL MmWorkingSetList
;
22 ULONG MmMaximumDeadKernelStacks
= 5;
23 SLIST_HEADER MmDeadStackSListHead
;
25 /* PRIVATE FUNCTIONS **********************************************************/
29 MiRosTakeOverSharedUserPage(IN PEPROCESS Process
)
32 PMEMORY_AREA MemoryArea
;
33 PVOID AllocatedBase
= (PVOID
)MM_SHARED_USER_DATA_VA
;
35 Status
= MmCreateMemoryArea(&Process
->Vm
,
36 MEMORY_AREA_OWNED_BY_ARM3
,
44 ASSERT(NT_SUCCESS(Status
));
49 MiCreatePebOrTeb(IN PEPROCESS Process
,
53 PETHREAD Thread
= PsGetCurrentThread();
57 ULONG_PTR StartAddress
, EndAddress
;
58 LARGE_INTEGER CurrentTime
;
59 TABLE_SEARCH_RESULT Result
= TableFoundNode
;
60 PMMADDRESS_NODE Parent
;
63 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'ldaV');
64 if (!Vad
) return STATUS_NO_MEMORY
;
66 /* Setup the primary flags with the size, and make it commited, private, RW */
68 Vad
->u
.VadFlags
.CommitCharge
= BYTES_TO_PAGES(Size
);
69 Vad
->u
.VadFlags
.MemCommit
= TRUE
;
70 Vad
->u
.VadFlags
.PrivateMemory
= TRUE
;
71 Vad
->u
.VadFlags
.Protection
= MM_READWRITE
;
72 Vad
->u
.VadFlags
.NoChange
= TRUE
;
74 /* Setup the secondary flags to make it a secured, writable, long VAD */
75 Vad
->u2
.LongFlags2
= 0;
76 Vad
->u2
.VadFlags2
.OneSecured
= TRUE
;
77 Vad
->u2
.VadFlags2
.LongVad
= TRUE
;
78 Vad
->u2
.VadFlags2
.ReadOnly
= FALSE
;
80 /* Lock the process address space */
81 KeAcquireGuardedMutex(&Process
->AddressCreationLock
);
83 /* Check if this is a PEB creation */
84 if (Size
== sizeof(PEB
))
86 /* Start at the highest valid address */
87 StartAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
89 /* Select the random coefficient */
90 KeQueryTickCount(&CurrentTime
);
91 CurrentTime
.LowPart
&= ((64 * _1KB
) >> PAGE_SHIFT
) - 1;
92 if (CurrentTime
.LowPart
<= 1) CurrentTime
.LowPart
= 2;
93 RandomCoeff
= CurrentTime
.LowPart
<< PAGE_SHIFT
;
95 /* Select the highest valid address minus the random coefficient */
96 StartAddress
-= RandomCoeff
;
97 EndAddress
= StartAddress
+ ROUND_TO_PAGES(Size
) - 1;
99 /* Try to find something below the random upper margin */
100 Result
= MiFindEmptyAddressRangeDownTree(ROUND_TO_PAGES(Size
),
108 /* Check for success. TableFoundNode means nothing free. */
109 if (Result
== TableFoundNode
)
111 /* For TEBs, or if a PEB location couldn't be found, scan the VAD root */
112 Result
= MiFindEmptyAddressRangeDownTree(ROUND_TO_PAGES(Size
),
113 (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
,
118 /* Bail out, if still nothing free was found */
119 if (Result
== TableFoundNode
)
121 ExFreePoolWithTag(Vad
, 'ldaV');
122 return STATUS_NO_MEMORY
;
126 /* Validate that it came from the VAD ranges */
127 ASSERT(*Base
>= (ULONG_PTR
)MI_LOWEST_VAD_ADDRESS
);
129 /* Build the rest of the VAD now */
130 Vad
->StartingVpn
= (*Base
) >> PAGE_SHIFT
;
131 Vad
->EndingVpn
= ((*Base
) + Size
- 1) >> PAGE_SHIFT
;
132 Vad
->u3
.Secured
.StartVpn
= *Base
;
133 Vad
->u3
.Secured
.EndVpn
= (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1);
134 Vad
->u1
.Parent
= NULL
;
136 /* FIXME: Should setup VAD bitmap */
137 Status
= STATUS_SUCCESS
;
139 /* Pretend as if we own the working set */
140 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
143 ASSERT(Vad
->EndingVpn
>= Vad
->StartingVpn
);
144 Process
->VadRoot
.NodeHint
= Vad
;
145 Vad
->ControlArea
= NULL
; // For Memory-Area hack
146 Vad
->FirstPrototypePte
= NULL
;
147 DPRINT("VAD: %p\n", Vad
);
148 DPRINT("Allocated PEB/TEB at: 0x%p for %16s\n", *Base
, Process
->ImageFileName
);
149 MiInsertNode(&Process
->VadRoot
, (PVOID
)Vad
, Parent
, Result
);
151 /* Release the working set */
152 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
154 /* Release the address space lock */
155 KeReleaseGuardedMutex(&Process
->AddressCreationLock
);
157 /* Return the status */
163 MmDeleteTeb(IN PEPROCESS Process
,
167 PETHREAD Thread
= PsGetCurrentThread();
169 PMM_AVL_TABLE VadTree
= &Process
->VadRoot
;
170 DPRINT("Deleting TEB: %p in %16s\n", Teb
, Process
->ImageFileName
);
172 /* TEB is one page */
173 TebEnd
= (ULONG_PTR
)Teb
+ ROUND_TO_PAGES(sizeof(TEB
)) - 1;
175 /* Attach to the process */
176 KeAttachProcess(&Process
->Pcb
);
178 /* Lock the process address space */
179 KeAcquireGuardedMutex(&Process
->AddressCreationLock
);
181 /* Find the VAD, make sure it's a TEB VAD */
182 Vad
= MiLocateAddress(Teb
);
183 DPRINT("Removing node for VAD: %lx %lx\n", Vad
->StartingVpn
, Vad
->EndingVpn
);
185 if (Vad
->StartingVpn
!= ((ULONG_PTR
)Teb
>> PAGE_SHIFT
))
187 /* Bug in the AVL code? */
188 DPRINT1("Corrupted VAD!\n");
192 /* Sanity checks for a valid TEB VAD */
193 ASSERT((Vad
->StartingVpn
== ((ULONG_PTR
)Teb
>> PAGE_SHIFT
) &&
194 (Vad
->EndingVpn
== (TebEnd
>> PAGE_SHIFT
))));
195 ASSERT(Vad
->u
.VadFlags
.NoChange
== TRUE
);
196 ASSERT(Vad
->u2
.VadFlags2
.OneSecured
== TRUE
);
197 ASSERT(Vad
->u2
.VadFlags2
.MultipleSecured
== FALSE
);
199 /* Lock the working set */
200 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
202 /* Remove this VAD from the tree */
203 ASSERT(VadTree
->NumberGenericTableElements
>= 1);
204 MiRemoveNode((PMMADDRESS_NODE
)Vad
, VadTree
);
206 /* Delete the pages */
207 MiDeleteVirtualAddresses((ULONG_PTR
)Teb
, TebEnd
, NULL
);
209 /* Release the working set */
210 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
216 /* Release the address space lock */
217 KeReleaseGuardedMutex(&Process
->AddressCreationLock
);
225 MmDeleteKernelStack(IN PVOID StackBase
,
229 PFN_NUMBER PageFrameNumber
, PageTableFrameNumber
;
230 PFN_COUNT StackPages
;
236 // This should be the guard page, so decrement by one
238 PointerPte
= MiAddressToPte(StackBase
);
242 // If this is a small stack, just push the stack onto the dead stack S-LIST
246 if (ExQueryDepthSList(&MmDeadStackSListHead
) < MmMaximumDeadKernelStacks
)
248 Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
249 InterlockedPushEntrySList(&MmDeadStackSListHead
,
250 (PSLIST_ENTRY
)&Pfn1
->u1
.NextStackPfn
);
256 // Calculate pages used
258 StackPages
= BYTES_TO_PAGES(GuiStack
?
259 KERNEL_LARGE_STACK_SIZE
: KERNEL_STACK_SIZE
);
261 /* Acquire the PFN lock */
262 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
267 for (i
= 0; i
< StackPages
; i
++)
270 // Check if this is a valid PTE
272 if (PointerPte
->u
.Hard
.Valid
== 1)
274 /* Get the PTE's page */
275 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
276 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
278 /* Now get the page of the page table mapping it */
279 PageTableFrameNumber
= Pfn1
->u4
.PteFrame
;
280 Pfn2
= MiGetPfnEntry(PageTableFrameNumber
);
282 /* Remove a shared reference, since the page is going away */
283 MiDecrementShareCount(Pfn2
, PageTableFrameNumber
);
285 /* Set the special pending delete marker */
286 MI_SET_PFN_DELETED(Pfn1
);
288 /* And now delete the actual stack page */
289 MiDecrementShareCount(Pfn1
, PageFrameNumber
);
299 // We should be at the guard page now
301 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
303 /* Release the PFN lock */
304 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
309 MiReleaseSystemPtes(PointerPte
, StackPages
+ 1, SystemPteSpace
);
314 MmCreateKernelStack(IN BOOLEAN GuiStack
,
317 PFN_COUNT StackPtes
, StackPages
;
318 PMMPTE PointerPte
, StackPte
;
320 MMPTE TempPte
, InvalidPte
;
322 PFN_NUMBER PageFrameIndex
;
327 // Calculate pages needed
332 // We'll allocate 64KB stack, but only commit 12K
334 StackPtes
= BYTES_TO_PAGES(KERNEL_LARGE_STACK_SIZE
);
335 StackPages
= BYTES_TO_PAGES(KERNEL_LARGE_STACK_COMMIT
);
341 // If the dead stack S-LIST has a stack on it, use it instead of allocating
342 // new system PTEs for this stack
344 if (ExQueryDepthSList(&MmDeadStackSListHead
))
346 Pfn1
= (PMMPFN
)InterlockedPopEntrySList(&MmDeadStackSListHead
);
349 PointerPte
= Pfn1
->PteAddress
;
350 BaseAddress
= MiPteToAddress(++PointerPte
);
356 // We'll allocate 12K and that's it
358 StackPtes
= BYTES_TO_PAGES(KERNEL_STACK_SIZE
);
359 StackPages
= StackPtes
;
363 // Reserve stack pages, plus a guard page
365 StackPte
= MiReserveSystemPtes(StackPtes
+ 1, SystemPteSpace
);
366 if (!StackPte
) return NULL
;
369 // Get the stack address
371 BaseAddress
= MiPteToAddress(StackPte
+ StackPtes
+ 1);
374 // Select the right PTE address where we actually start committing pages
376 PointerPte
= StackPte
;
377 if (GuiStack
) PointerPte
+= BYTES_TO_PAGES(KERNEL_LARGE_STACK_SIZE
-
378 KERNEL_LARGE_STACK_COMMIT
);
381 /* Setup the temporary invalid PTE */
382 MI_MAKE_SOFTWARE_PTE(&InvalidPte
, MM_NOACCESS
);
384 /* Setup the template stack PTE */
385 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte
, PointerPte
+ 1, MM_READWRITE
, 0);
388 // Acquire the PFN DB lock
390 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
393 // Loop each stack page
395 for (i
= 0; i
< StackPages
; i
++)
402 /* Get a page and write the current invalid PTE */
403 MI_SET_USAGE(MI_USAGE_KERNEL_STACK
);
404 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName
);
405 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
406 MI_WRITE_INVALID_PTE(PointerPte
, InvalidPte
);
408 /* Initialize the PFN entry for this page */
409 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
411 /* Write the valid PTE */
412 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
413 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
417 // Release the PFN lock
419 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
422 // Return the stack address
429 MmGrowKernelStackEx(IN PVOID StackPointer
,
432 PKTHREAD Thread
= KeGetCurrentThread();
433 PMMPTE LimitPte
, NewLimitPte
, LastPte
;
435 MMPTE TempPte
, InvalidPte
;
436 PFN_NUMBER PageFrameIndex
;
439 // Make sure the stack did not overflow
441 ASSERT(((ULONG_PTR
)Thread
->StackBase
- (ULONG_PTR
)Thread
->StackLimit
) <=
442 (KERNEL_LARGE_STACK_SIZE
+ PAGE_SIZE
));
445 // Get the current stack limit
447 LimitPte
= MiAddressToPte(Thread
->StackLimit
);
448 ASSERT(LimitPte
->u
.Hard
.Valid
== 1);
451 // Get the new one and make sure this isn't a retarded request
453 NewLimitPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)StackPointer
- GrowSize
));
454 if (NewLimitPte
== LimitPte
) return STATUS_SUCCESS
;
457 // Now make sure you're not going past the reserved space
459 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)Thread
->StackBase
-
460 KERNEL_LARGE_STACK_SIZE
));
461 if (NewLimitPte
< LastPte
)
466 DPRINT1("Thread wants too much stack\n");
467 return STATUS_STACK_OVERFLOW
;
471 // Calculate the number of new pages
475 /* Setup the temporary invalid PTE */
476 MI_MAKE_SOFTWARE_PTE(&InvalidPte
, MM_NOACCESS
);
479 // Acquire the PFN DB lock
481 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
484 // Loop each stack page
486 while (LimitPte
>= NewLimitPte
)
488 /* Get a page and write the current invalid PTE */
489 MI_SET_USAGE(MI_USAGE_KERNEL_STACK_EXPANSION
);
490 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName
);
491 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
492 MI_WRITE_INVALID_PTE(LimitPte
, InvalidPte
);
494 /* Initialize the PFN entry for this page */
495 MiInitializePfn(PageFrameIndex
, LimitPte
, 1);
497 /* Setup the template stack PTE */
498 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte
, LimitPte
, MM_READWRITE
, PageFrameIndex
);
500 /* Write the valid PTE */
501 MI_WRITE_VALID_PTE(LimitPte
--, TempPte
);
505 // Release the PFN lock
507 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
512 Thread
->StackLimit
= (ULONG_PTR
)MiPteToAddress(NewLimitPte
);
513 return STATUS_SUCCESS
;
518 MmGrowKernelStack(IN PVOID StackPointer
)
521 // Call the extended version
523 return MmGrowKernelStackEx(StackPointer
, KERNEL_LARGE_STACK_COMMIT
);
528 MmSetMemoryPriorityProcess(IN PEPROCESS Process
,
529 IN UCHAR MemoryPriority
)
534 // Check if we have less then 16MB of Physical Memory
536 if ((MmSystemSize
== MmSmallSystem
) &&
537 (MmNumberOfPhysicalPages
< ((15 * 1024 * 1024) / PAGE_SIZE
)))
540 // Always use background priority
542 MemoryPriority
= MEMORY_PRIORITY_BACKGROUND
;
546 // Save the old priority and update it
548 OldPriority
= (UCHAR
)Process
->Vm
.Flags
.MemoryPriority
;
549 Process
->Vm
.Flags
.MemoryPriority
= MemoryPriority
;
552 // Return the old priority
559 MmCreatePeb(IN PEPROCESS Process
,
560 IN PINITIAL_PEB InitialPeb
,
564 LARGE_INTEGER SectionOffset
;
566 PVOID TableBase
= NULL
;
567 PIMAGE_NT_HEADERS NtHeaders
;
568 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData
;
570 USHORT Characteristics
;
571 KAFFINITY ProcessAffinityMask
= 0;
572 SectionOffset
.QuadPart
= (ULONGLONG
)0;
578 KeAttachProcess(&Process
->Pcb
);
583 Status
= MmMapViewOfSection(ExpNlsSectionPointer
,
593 DPRINT("NLS Tables at: %p\n", TableBase
);
594 if (!NT_SUCCESS(Status
))
596 /* Cleanup and exit */
604 Status
= MiCreatePebOrTeb(Process
, sizeof(PEB
), (PULONG_PTR
)&Peb
);
605 DPRINT("PEB at: %p\n", Peb
);
606 if (!NT_SUCCESS(Status
))
608 /* Cleanup and exit */
614 // Use SEH in case we can't load the PEB
619 // Initialize the PEB
621 RtlZeroMemory(Peb
, sizeof(PEB
));
626 Peb
->ImageBaseAddress
= Process
->SectionBaseAddress
;
627 Peb
->InheritedAddressSpace
= InitialPeb
->InheritedAddressSpace
;
628 Peb
->Mutant
= InitialPeb
->Mutant
;
629 Peb
->ImageUsesLargePages
= InitialPeb
->ImageUsesLargePages
;
634 Peb
->AnsiCodePageData
= (PCHAR
)TableBase
+ ExpAnsiCodePageDataOffset
;
635 Peb
->OemCodePageData
= (PCHAR
)TableBase
+ ExpOemCodePageDataOffset
;
636 Peb
->UnicodeCaseTableData
= (PCHAR
)TableBase
+ ExpUnicodeCaseTableDataOffset
;
639 // Default Version Data (could get changed below)
641 Peb
->OSMajorVersion
= NtMajorVersion
;
642 Peb
->OSMinorVersion
= NtMinorVersion
;
643 Peb
->OSBuildNumber
= (USHORT
)(NtBuildNumber
& 0x3FFF);
644 Peb
->OSPlatformId
= VER_PLATFORM_WIN32_NT
;
645 Peb
->OSCSDVersion
= (USHORT
)CmNtCSDVersion
;
648 // Heap and Debug Data
650 Peb
->NumberOfProcessors
= KeNumberProcessors
;
651 Peb
->BeingDebugged
= (BOOLEAN
)(Process
->DebugPort
!= NULL
);
652 Peb
->NtGlobalFlag
= NtGlobalFlag
;
653 Peb
->HeapSegmentReserve
= MmHeapSegmentReserve
;
654 Peb
->HeapSegmentCommit
= MmHeapSegmentCommit
;
655 Peb
->HeapDeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
656 Peb
->HeapDeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
657 Peb
->CriticalSectionTimeout
= MmCriticalSectionTimeout
;
658 Peb
->MinimumStackCommit
= MmMinimumStackCommitInBytes
;
659 Peb
->MaximumNumberOfHeaps
= (PAGE_SIZE
- sizeof(PEB
)) / sizeof(PVOID
);
660 Peb
->ProcessHeaps
= (PVOID
*)(Peb
+ 1);
665 if (Process
->Session
) Peb
->SessionId
= MmGetSessionId(Process
);
667 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
673 _SEH2_YIELD(return _SEH2_GetExceptionCode());
678 // Use SEH in case we can't load the image
685 NtHeaders
= RtlImageNtHeader(Peb
->ImageBaseAddress
);
686 Characteristics
= NtHeaders
->FileHeader
.Characteristics
;
688 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
694 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT
);
704 // Use SEH in case we can't load the headers
709 // Get the Image Config Data too
711 ImageConfigData
= RtlImageDirectoryEntryToData(Peb
->ImageBaseAddress
,
713 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
,
720 ProbeForRead(ImageConfigData
,
721 sizeof(IMAGE_LOAD_CONFIG_DIRECTORY
),
726 // Write subsystem data
728 Peb
->ImageSubsystem
= NtHeaders
->OptionalHeader
.Subsystem
;
729 Peb
->ImageSubsystemMajorVersion
= NtHeaders
->OptionalHeader
.MajorSubsystemVersion
;
730 Peb
->ImageSubsystemMinorVersion
= NtHeaders
->OptionalHeader
.MinorSubsystemVersion
;
733 // Check for version data
735 if (NtHeaders
->OptionalHeader
.Win32VersionValue
)
738 // Extract values and write them
740 Peb
->OSMajorVersion
= NtHeaders
->OptionalHeader
.Win32VersionValue
& 0xFF;
741 Peb
->OSMinorVersion
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 8) & 0xFF;
742 Peb
->OSBuildNumber
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 16) & 0x3FFF;
743 Peb
->OSPlatformId
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 30) ^ 2;
745 /* Process CSD version override */
746 if ((ImageConfigData
) && (ImageConfigData
->CSDVersion
))
748 /* Take the value from the image configuration directory */
749 Peb
->OSCSDVersion
= ImageConfigData
->CSDVersion
;
753 /* Process optional process affinity mask override */
754 if ((ImageConfigData
) && (ImageConfigData
->ProcessAffinityMask
))
756 /* Take the value from the image configuration directory */
757 ProcessAffinityMask
= ImageConfigData
->ProcessAffinityMask
;
761 // Check if this is a UP image
762 if (Characteristics
& IMAGE_FILE_UP_SYSTEM_ONLY
)
765 // Force it to use CPU 0
767 /* FIXME: this should use the MmRotatingUniprocessorNumber */
768 Peb
->ImageProcessAffinityMask
= 0;
773 // Whatever was configured
775 Peb
->ImageProcessAffinityMask
= ProcessAffinityMask
;
778 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
784 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT
);
790 // Detach from the Process
794 return STATUS_SUCCESS
;
799 MmCreateTeb(IN PEPROCESS Process
,
800 IN PCLIENT_ID ClientId
,
801 IN PINITIAL_TEB InitialTeb
,
805 NTSTATUS Status
= STATUS_SUCCESS
;
811 KeAttachProcess(&Process
->Pcb
);
816 Status
= MiCreatePebOrTeb(Process
, sizeof(TEB
), (PULONG_PTR
)&Teb
);
817 ASSERT(NT_SUCCESS(Status
));
820 // Use SEH in case we can't load the TEB
825 // Initialize the PEB
827 RtlZeroMemory(Teb
, sizeof(TEB
));
833 Teb
->NtTib
.ExceptionList
= NULL
;
835 Teb
->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
837 Teb
->NtTib
.Self
= (PNT_TIB
)Teb
;
840 // Identify this as an OS/2 V3.0 ("Cruiser") TIB
842 Teb
->NtTib
.Version
= 30 << 8;
847 Teb
->ClientId
= *ClientId
;
848 Teb
->RealClientId
= *ClientId
;
849 Teb
->ProcessEnvironmentBlock
= Process
->Peb
;
850 Teb
->CurrentLocale
= PsDefaultThreadLocaleId
;
853 // Check if we have a grandparent TEB
855 if ((InitialTeb
->PreviousStackBase
== NULL
) &&
856 (InitialTeb
->PreviousStackLimit
== NULL
))
859 // Use initial TEB values
861 Teb
->NtTib
.StackBase
= InitialTeb
->StackBase
;
862 Teb
->NtTib
.StackLimit
= InitialTeb
->StackLimit
;
863 Teb
->DeallocationStack
= InitialTeb
->AllocatedStackBase
;
868 // Use grandparent TEB values
870 Teb
->NtTib
.StackBase
= InitialTeb
->PreviousStackBase
;
871 Teb
->NtTib
.StackLimit
= InitialTeb
->PreviousStackLimit
;
875 // Initialize the static unicode string
877 Teb
->StaticUnicodeString
.MaximumLength
= sizeof(Teb
->StaticUnicodeBuffer
);
878 Teb
->StaticUnicodeString
.Buffer
= Teb
->StaticUnicodeBuffer
;
880 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
885 Status
= _SEH2_GetExceptionCode();
899 MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess
)
905 /* Setup some bogus list data */
906 MmWorkingSetList
->LastEntry
= CurrentProcess
->Vm
.MinimumWorkingSetSize
;
907 MmWorkingSetList
->HashTable
= NULL
;
908 MmWorkingSetList
->HashTableSize
= 0;
909 MmWorkingSetList
->NumberOfImageWaiters
= 0;
910 MmWorkingSetList
->Wsle
= (PVOID
)0xDEADBABE;
911 MmWorkingSetList
->VadBitMapHint
= 1;
912 MmWorkingSetList
->HashTableStart
= (PVOID
)0xBADAB00B;
913 MmWorkingSetList
->HighestPermittedHashAddress
= (PVOID
)0xCAFEBABE;
914 MmWorkingSetList
->FirstFree
= 1;
915 MmWorkingSetList
->FirstDynamic
= 2;
916 MmWorkingSetList
->NextSlot
= 3;
917 MmWorkingSetList
->LastInitializedWsle
= 4;
919 /* The rule is that the owner process is always in the FLINK of the PDE's PFN entry */
920 Pfn1
= MiGetPfnEntry(CurrentProcess
->Pcb
.DirectoryTableBase
[0] >> PAGE_SHIFT
);
921 ASSERT(Pfn1
->u4
.PteFrame
== MiGetPfnEntryIndex(Pfn1
));
922 Pfn1
->u1
.Event
= (PKEVENT
)CurrentProcess
;
924 /* Map the process working set in kernel space */
925 sysPte
= MiReserveSystemPtes(1, SystemPteSpace
);
926 MI_MAKE_HARDWARE_PTE_KERNEL(&tempPte
, sysPte
, MM_READWRITE
, CurrentProcess
->WorkingSetPage
);
927 MI_WRITE_VALID_PTE(sysPte
, tempPte
);
928 CurrentProcess
->Vm
.VmWorkingSetList
= MiPteToAddress(sysPte
);
933 MmInitializeProcessAddressSpace(IN PEPROCESS Process
,
934 IN PEPROCESS ProcessClone OPTIONAL
,
935 IN PVOID Section OPTIONAL
,
937 IN POBJECT_NAME_INFORMATION
*AuditName OPTIONAL
)
939 NTSTATUS Status
= STATUS_SUCCESS
;
942 PROS_SECTION_OBJECT SectionObject
= Section
;
946 PFN_NUMBER PageFrameNumber
;
947 UNICODE_STRING FileName
;
953 /* We should have a PDE */
954 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] != 0);
955 ASSERT(Process
->PdeUpdateNeeded
== FALSE
);
957 /* Attach to the process */
958 KeAttachProcess(&Process
->Pcb
);
960 /* The address space should now been in phase 1 or 0 */
961 ASSERT(Process
->AddressSpaceInitialized
<= 1);
962 Process
->AddressSpaceInitialized
= 2;
964 /* Initialize the Addresss Space lock */
965 KeInitializeGuardedMutex(&Process
->AddressCreationLock
);
966 Process
->Vm
.WorkingSetExpansionLinks
.Flink
= NULL
;
968 /* Initialize AVL tree */
969 ASSERT(Process
->VadRoot
.NumberGenericTableElements
== 0);
970 Process
->VadRoot
.BalancedRoot
.u1
.Parent
= &Process
->VadRoot
.BalancedRoot
;
972 /* Lock PFN database */
973 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
975 /* Setup the PFN for the PDE base of this process */
977 PointerPte
= MiAddressToPte(PXE_BASE
);
979 PointerPte
= MiAddressToPte(PDE_BASE
);
981 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
982 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] == PageFrameNumber
* PAGE_SIZE
);
983 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
985 /* Do the same for hyperspace */
987 PointerPde
= MiAddressToPxe((PVOID
)HYPER_SPACE
);
989 PointerPde
= MiAddressToPde(HYPER_SPACE
);
991 PageFrameNumber
= PFN_FROM_PTE(PointerPde
);
992 //ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE); // we're not lucky
993 MiInitializePfn(PageFrameNumber
, (PMMPTE
)PointerPde
, TRUE
);
995 /* Setup the PFN for the PTE for the working set */
996 PointerPte
= MiAddressToPte(MI_WORKING_SET_LIST
);
997 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, MM_READWRITE
, 0);
998 ASSERT(PointerPte
->u
.Long
!= 0);
999 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
1000 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPte
);
1001 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
1002 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
1003 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
1005 /* Now initialize the working set list */
1006 MiInitializeWorkingSetList(Process
);
1009 ASSERT(Process
->PhysicalVadRoot
== NULL
);
1011 /* Release PFN lock */
1012 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1014 /* Lock the VAD, ARM3-owned ranges away */
1015 MiRosTakeOverSharedUserPage(Process
);
1017 /* Check if there's a Section Object */
1020 /* Determine the image file name and save it to EPROCESS */
1021 FileName
= SectionObject
->FileObject
->FileName
;
1022 Source
= (PWCHAR
)((PCHAR
)FileName
.Buffer
+ FileName
.Length
);
1023 if (FileName
.Buffer
)
1025 /* Loop the file name*/
1026 while (Source
> FileName
.Buffer
)
1028 /* Make sure this isn't a backslash */
1029 if (*--Source
== OBJ_NAME_PATH_SEPARATOR
)
1031 /* If so, stop it here */
1037 /* Otherwise, keep going */
1043 /* Copy the to the process and truncate it to 15 characters if necessary */
1044 Destination
= Process
->ImageFileName
;
1045 Length
= min(Length
, sizeof(Process
->ImageFileName
) - 1);
1046 while (Length
--) *Destination
++ = (UCHAR
)*Source
++;
1047 *Destination
= ANSI_NULL
;
1049 /* Check if caller wants an audit name */
1052 /* Setup the audit name */
1053 Status
= SeInitializeProcessAuditName(SectionObject
->FileObject
,
1056 if (!NT_SUCCESS(Status
))
1064 /* Map the section */
1065 Status
= MmMapViewOfSection(Section
,
1076 /* Save the pointer */
1077 Process
->SectionBaseAddress
= ImageBase
;
1080 /* Be nice and detach */
1083 /* Return status to caller */
1090 MmInitializeHandBuiltProcess(IN PEPROCESS Process
,
1091 IN PULONG_PTR DirectoryTableBase
)
1093 /* Share the directory base with the idle process */
1094 DirectoryTableBase
[0] = PsGetCurrentProcess()->Pcb
.DirectoryTableBase
[0];
1095 DirectoryTableBase
[1] = PsGetCurrentProcess()->Pcb
.DirectoryTableBase
[1];
1097 /* Initialize the Addresss Space */
1098 KeInitializeGuardedMutex(&Process
->AddressCreationLock
);
1099 KeInitializeSpinLock(&Process
->HyperSpaceLock
);
1100 Process
->Vm
.WorkingSetExpansionLinks
.Flink
= NULL
;
1101 ASSERT(Process
->VadRoot
.NumberGenericTableElements
== 0);
1102 Process
->VadRoot
.BalancedRoot
.u1
.Parent
= &Process
->VadRoot
.BalancedRoot
;
1104 /* Use idle process Working set */
1105 Process
->Vm
.VmWorkingSetList
= PsGetCurrentProcess()->Vm
.VmWorkingSetList
;
1108 Process
->HasAddressSpace
= TRUE
;//??
1109 return STATUS_SUCCESS
;
1115 MmInitializeHandBuiltProcess2(IN PEPROCESS Process
)
1117 /* Lock the VAD, ARM3-owned ranges away */
1118 MiRosTakeOverSharedUserPage(Process
);
1119 return STATUS_SUCCESS
;
1123 /* FIXME: Evaluate ways to make this portable yet arch-specific */
1126 MmCreateProcessAddressSpace(IN ULONG MinWs
,
1127 IN PEPROCESS Process
,
1128 OUT PULONG_PTR DirectoryTableBase
)
1131 PFN_NUMBER PdeIndex
, HyperIndex
, WsListIndex
;
1133 MMPTE TempPte
, PdePte
;
1135 PMMPTE SystemTable
, HyperTable
;
1139 /* Choose a process color */
1140 Process
->NextPageColor
= (USHORT
)RtlRandom(&MmProcessColorSeed
);
1142 /* Setup the hyperspace lock */
1143 KeInitializeSpinLock(&Process
->HyperSpaceLock
);
1145 /* Lock PFN database */
1146 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1148 /* Get a zero page for the PDE, if possible */
1149 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1150 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY
);
1151 PdeIndex
= MiRemoveZeroPageSafe(Color
);
1154 /* No zero pages, grab a free one */
1155 PdeIndex
= MiRemoveAnyPage(Color
);
1157 /* Zero it outside the PFN lock */
1158 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1159 MiZeroPhysicalPage(PdeIndex
);
1160 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1163 /* Get a zero page for hyperspace, if possible */
1164 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY
);
1165 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1166 HyperIndex
= MiRemoveZeroPageSafe(Color
);
1169 /* No zero pages, grab a free one */
1170 HyperIndex
= MiRemoveAnyPage(Color
);
1172 /* Zero it outside the PFN lock */
1173 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1174 MiZeroPhysicalPage(HyperIndex
);
1175 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1178 /* Get a zero page for the woring set list, if possible */
1179 MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
1180 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1181 WsListIndex
= MiRemoveZeroPageSafe(Color
);
1184 /* No zero pages, grab a free one */
1185 WsListIndex
= MiRemoveAnyPage(Color
);
1187 /* Zero it outside the PFN lock */
1188 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1189 MiZeroPhysicalPage(WsListIndex
);
1193 /* Release the PFN lock */
1194 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1197 /* Switch to phase 1 initialization */
1198 ASSERT(Process
->AddressSpaceInitialized
== 0);
1199 Process
->AddressSpaceInitialized
= 1;
1201 /* Set the base directory pointers */
1202 Process
->WorkingSetPage
= WsListIndex
;
1203 DirectoryTableBase
[0] = PdeIndex
<< PAGE_SHIFT
;
1204 DirectoryTableBase
[1] = HyperIndex
<< PAGE_SHIFT
;
1206 /* Make sure we don't already have a page directory setup */
1207 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] == 0);
1209 /* Get a PTE to map hyperspace */
1210 PointerPte
= MiReserveSystemPtes(1, SystemPteSpace
);
1211 ASSERT(PointerPte
!= NULL
);
1214 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte
,
1219 /* Set it dirty and map it */
1220 MI_MAKE_DIRTY_PAGE(&PdePte
);
1221 MI_WRITE_VALID_PTE(PointerPte
, PdePte
);
1223 /* Now get hyperspace's page table */
1224 HyperTable
= MiPteToAddress(PointerPte
);
1226 /* Now write the PTE/PDE entry for the working set list index itself */
1227 TempPte
= ValidKernelPte
;
1228 TempPte
.u
.Hard
.PageFrameNumber
= WsListIndex
;
1229 /* Hyperspace is local */
1230 MI_MAKE_LOCAL_PAGE(&TempPte
);
1231 PdeOffset
= MiAddressToPteOffset(MmWorkingSetList
);
1232 HyperTable
[PdeOffset
] = TempPte
;
1234 /* Let go of the system PTE */
1235 MiReleaseSystemPtes(PointerPte
, 1, SystemPteSpace
);
1237 /* Save the PTE address of the page directory itself */
1238 Pfn1
= MiGetPfnEntry(PdeIndex
);
1239 Pfn1
->PteAddress
= (PMMPTE
)PDE_BASE
;
1241 /* Insert us into the Mm process list */
1242 InsertTailList(&MmProcessList
, &Process
->MmProcessLinks
);
1244 /* Get a PTE to map the page directory */
1245 PointerPte
= MiReserveSystemPtes(1, SystemPteSpace
);
1246 ASSERT(PointerPte
!= NULL
);
1249 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte
,
1254 /* Set it dirty and map it */
1255 MI_MAKE_DIRTY_PAGE(&PdePte
);
1256 MI_WRITE_VALID_PTE(PointerPte
, PdePte
);
1258 /* Now get the page directory (which we'll double map, so call it a page table */
1259 SystemTable
= MiPteToAddress(PointerPte
);
1261 /* Copy all the kernel mappings */
1262 PdeOffset
= MiGetPdeOffset(MmSystemRangeStart
);
1263 RtlCopyMemory(&SystemTable
[PdeOffset
],
1264 MiAddressToPde(MmSystemRangeStart
),
1265 PAGE_SIZE
- PdeOffset
* sizeof(MMPTE
));
1267 /* Now write the PTE/PDE entry for hyperspace itself */
1268 TempPte
= ValidKernelPte
;
1269 TempPte
.u
.Hard
.PageFrameNumber
= HyperIndex
;
1270 PdeOffset
= MiGetPdeOffset(HYPER_SPACE
);
1271 SystemTable
[PdeOffset
] = TempPte
;
1275 ASSERT(MiGetPdeOffset(MmHyperSpaceEnd
) >= PdeOffset
);
1277 /* Now do the x86 trick of making the PDE a page table itself */
1278 PdeOffset
= MiGetPdeOffset(PTE_BASE
);
1279 TempPte
.u
.Hard
.PageFrameNumber
= PdeIndex
;
1280 SystemTable
[PdeOffset
] = TempPte
;
1282 /* Let go of the system PTE */
1283 MiReleaseSystemPtes(PointerPte
, 1, SystemPteSpace
);
1285 /* Add the process to the session */
1286 MiSessionAddProcess(Process
);
1293 MmCleanProcessAddressSpace(IN PEPROCESS Process
)
1296 PMM_AVL_TABLE VadTree
;
1297 PETHREAD Thread
= PsGetCurrentThread();
1299 /* Only support this */
1300 ASSERT(Process
->AddressSpaceInitialized
== 2);
1302 /* Remove from the session */
1303 MiSessionRemoveProcess();
1305 /* Lock the process address space from changes */
1306 MmLockAddressSpace(&Process
->Vm
);
1307 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1309 /* VM is deleted now */
1310 Process
->VmDeleted
= TRUE
;
1311 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1313 /* Enumerate the VADs */
1314 VadTree
= &Process
->VadRoot
;
1315 while (VadTree
->NumberGenericTableElements
)
1317 /* Grab the current VAD */
1318 Vad
= (PMMVAD
)VadTree
->BalancedRoot
.RightChild
;
1320 /* Lock the working set */
1321 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1323 /* Remove this VAD from the tree */
1324 ASSERT(VadTree
->NumberGenericTableElements
>= 1);
1325 MiRemoveNode((PMMADDRESS_NODE
)Vad
, VadTree
);
1327 /* Only regular VADs supported for now */
1328 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1330 /* Check if this is a section VAD */
1331 if (!(Vad
->u
.VadFlags
.PrivateMemory
) && (Vad
->ControlArea
))
1333 /* Remove the view */
1334 MiRemoveMappedView(Process
, Vad
);
1338 /* Delete the addresses */
1339 MiDeleteVirtualAddresses(Vad
->StartingVpn
<< PAGE_SHIFT
,
1340 (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1),
1343 /* Release the working set */
1344 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1347 /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */
1348 if (Vad
->u
.VadFlags
.Spare
== 1)
1350 /* Set a flag so MmDeleteMemoryArea knows to free, but not to remove */
1351 Vad
->u
.VadFlags
.Spare
= 2;
1355 /* Free the VAD memory */
1359 /* Lock the working set */
1360 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1361 ASSERT(Process
->CloneRoot
== NULL
);
1362 ASSERT(Process
->PhysicalVadRoot
== NULL
);
1364 /* Delete the shared user data section */
1365 MiDeleteVirtualAddresses(USER_SHARED_DATA
, USER_SHARED_DATA
, NULL
);
1367 /* Release the working set */
1368 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1370 /* Release the address space */
1371 MmUnlockAddressSpace(&Process
->Vm
);
1376 MmDeleteProcessAddressSpace2(IN PEPROCESS Process
)
1380 PFN_NUMBER PageFrameIndex
;
1382 //ASSERT(Process->CommitCharge == 0);
1384 /* Acquire the PFN lock */
1385 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1387 /* Check for fully initialized process */
1388 if (Process
->AddressSpaceInitialized
== 2)
1390 /* Map the working set page and its page table */
1391 Pfn1
= MiGetPfnEntry(Process
->WorkingSetPage
);
1392 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1395 MI_SET_PFN_DELETED(Pfn1
);
1396 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1397 MiDecrementShareCount(Pfn1
, Process
->WorkingSetPage
);
1398 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1399 MiReleaseSystemPtes(MiAddressToPte(Process
->Vm
.VmWorkingSetList
), 1, SystemPteSpace
);
1401 /* Now map hyperspace and its page table */
1402 PageFrameIndex
= Process
->Pcb
.DirectoryTableBase
[1] >> PAGE_SHIFT
;
1403 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1404 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1407 MI_SET_PFN_DELETED(Pfn1
);
1408 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1409 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1410 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1412 /* Finally, nuke the PDE itself */
1413 PageFrameIndex
= Process
->Pcb
.DirectoryTableBase
[0] >> PAGE_SHIFT
;
1414 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1415 MI_SET_PFN_DELETED(Pfn1
);
1416 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1417 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1419 /* Page table is now dead. Bye bye... */
1420 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1424 /* A partly-initialized process should never exit through here */
1428 /* Release the PFN lock */
1429 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1431 /* Drop a reference on the session */
1432 if (Process
->Session
) MiReleaseProcessReferenceToSessionDataPage(Process
->Session
);
1434 /* Clear out the PDE pages */
1435 Process
->Pcb
.DirectoryTableBase
[0] = 0;
1436 Process
->Pcb
.DirectoryTableBase
[1] = 0;
1440 /* SYSTEM CALLS ***************************************************************/
1444 NtAllocateUserPhysicalPages(IN HANDLE ProcessHandle
,
1445 IN OUT PULONG_PTR NumberOfPages
,
1446 IN OUT PULONG_PTR UserPfnArray
)
1449 return STATUS_NOT_IMPLEMENTED
;
1454 NtMapUserPhysicalPages(IN PVOID VirtualAddresses
,
1455 IN ULONG_PTR NumberOfPages
,
1456 IN OUT PULONG_PTR UserPfnArray
)
1459 return STATUS_NOT_IMPLEMENTED
;
1464 NtMapUserPhysicalPagesScatter(IN PVOID
*VirtualAddresses
,
1465 IN ULONG_PTR NumberOfPages
,
1466 IN OUT PULONG_PTR UserPfnArray
)
1469 return STATUS_NOT_IMPLEMENTED
;
1474 NtFreeUserPhysicalPages(IN HANDLE ProcessHandle
,
1475 IN OUT PULONG_PTR NumberOfPages
,
1476 IN OUT PULONG_PTR UserPfnArray
)
1479 return STATUS_NOT_IMPLEMENTED
;