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 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
,
43 ASSERT(NT_SUCCESS(Status
));
48 MiCreatePebOrTeb(IN PEPROCESS Process
,
50 OUT PULONG_PTR BaseAddress
)
54 ULONG_PTR HighestAddress
, RandomBase
;
56 LARGE_INTEGER CurrentTime
;
59 Vad
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(MMVAD_LONG
), 'ldaV');
60 if (!Vad
) return STATUS_NO_MEMORY
;
62 /* Setup the primary flags with the size, and make it commited, private, RW */
64 Vad
->u
.VadFlags
.CommitCharge
= BYTES_TO_PAGES(Size
);
65 Vad
->u
.VadFlags
.MemCommit
= TRUE
;
66 Vad
->u
.VadFlags
.PrivateMemory
= TRUE
;
67 Vad
->u
.VadFlags
.Protection
= MM_READWRITE
;
68 Vad
->u
.VadFlags
.NoChange
= TRUE
;
69 Vad
->u1
.Parent
= NULL
;
71 /* Setup the secondary flags to make it a secured, writable, long VAD */
72 Vad
->u2
.LongFlags2
= 0;
73 Vad
->u2
.VadFlags2
.OneSecured
= TRUE
;
74 Vad
->u2
.VadFlags2
.LongVad
= TRUE
;
75 Vad
->u2
.VadFlags2
.ReadOnly
= FALSE
;
77 Vad
->ControlArea
= NULL
; // For Memory-Area hack
78 Vad
->FirstPrototypePte
= NULL
;
80 /* Check if this is a PEB creation */
81 ASSERT(sizeof(TEB
) != sizeof(PEB
));
82 if (Size
== sizeof(PEB
))
84 /* Create a random value to select one page in a 64k region */
85 KeQueryTickCount(&CurrentTime
);
86 CurrentTime
.LowPart
&= (_64K
/ PAGE_SIZE
) - 1;
88 /* Calculate a random base address */
89 RandomBase
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1;
90 RandomBase
-= CurrentTime
.LowPart
<< PAGE_SHIFT
;
92 /* Make sure the base address is not too high */
93 AlignedSize
= ROUND_TO_PAGES(Size
);
94 if ((RandomBase
+ AlignedSize
) > (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1)
96 RandomBase
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
+ 1 - AlignedSize
;
99 /* Calculate the highest allowed address */
100 HighestAddress
= RandomBase
+ AlignedSize
- 1;
104 HighestAddress
= (ULONG_PTR
)MM_HIGHEST_VAD_ADDRESS
;
108 Status
= MiInsertVadEx((PMMVAD
)Vad
,
110 BYTES_TO_PAGES(Size
),
114 if (!NT_SUCCESS(Status
))
116 ExFreePoolWithTag(Vad
, 'ldaV');
117 return STATUS_NO_MEMORY
;
121 return STATUS_SUCCESS
;
126 MmDeleteTeb(IN PEPROCESS Process
,
130 PETHREAD Thread
= PsGetCurrentThread();
132 PMM_AVL_TABLE VadTree
= &Process
->VadRoot
;
133 DPRINT("Deleting TEB: %p in %16s\n", Teb
, Process
->ImageFileName
);
135 /* TEB is one page */
136 TebEnd
= (ULONG_PTR
)Teb
+ ROUND_TO_PAGES(sizeof(TEB
)) - 1;
138 /* Attach to the process */
139 KeAttachProcess(&Process
->Pcb
);
141 /* Lock the process address space */
142 KeAcquireGuardedMutex(&Process
->AddressCreationLock
);
144 /* Find the VAD, make sure it's a TEB VAD */
145 Vad
= MiLocateAddress(Teb
);
146 DPRINT("Removing node for VAD: %lx %lx\n", Vad
->StartingVpn
, Vad
->EndingVpn
);
148 if (Vad
->StartingVpn
!= ((ULONG_PTR
)Teb
>> PAGE_SHIFT
))
150 /* Bug in the AVL code? */
151 DPRINT1("Corrupted VAD!\n");
155 /* Sanity checks for a valid TEB VAD */
156 ASSERT((Vad
->StartingVpn
== ((ULONG_PTR
)Teb
>> PAGE_SHIFT
) &&
157 (Vad
->EndingVpn
== (TebEnd
>> PAGE_SHIFT
))));
158 ASSERT(Vad
->u
.VadFlags
.NoChange
== TRUE
);
159 ASSERT(Vad
->u2
.VadFlags2
.OneSecured
== TRUE
);
160 ASSERT(Vad
->u2
.VadFlags2
.MultipleSecured
== FALSE
);
162 /* Lock the working set */
163 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
165 /* Remove this VAD from the tree */
166 ASSERT(VadTree
->NumberGenericTableElements
>= 1);
167 MiRemoveNode((PMMADDRESS_NODE
)Vad
, VadTree
);
169 /* Delete the pages */
170 MiDeleteVirtualAddresses((ULONG_PTR
)Teb
, TebEnd
, NULL
);
172 /* Release the working set */
173 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
179 /* Release the address space lock */
180 KeReleaseGuardedMutex(&Process
->AddressCreationLock
);
188 MmDeleteKernelStack(IN PVOID StackBase
,
192 PFN_NUMBER PageFrameNumber
, PageTableFrameNumber
;
193 PFN_COUNT StackPages
;
199 // This should be the guard page, so decrement by one
201 PointerPte
= MiAddressToPte(StackBase
);
205 // If this is a small stack, just push the stack onto the dead stack S-LIST
209 if (ExQueryDepthSList(&MmDeadStackSListHead
) < MmMaximumDeadKernelStacks
)
211 Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
212 InterlockedPushEntrySList(&MmDeadStackSListHead
,
213 (PSLIST_ENTRY
)&Pfn1
->u1
.NextStackPfn
);
219 // Calculate pages used
221 StackPages
= BYTES_TO_PAGES(GuiStack
?
222 KERNEL_LARGE_STACK_SIZE
: KERNEL_STACK_SIZE
);
224 /* Acquire the PFN lock */
225 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
230 for (i
= 0; i
< StackPages
; i
++)
233 // Check if this is a valid PTE
235 if (PointerPte
->u
.Hard
.Valid
== 1)
237 /* Get the PTE's page */
238 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
239 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
241 /* Now get the page of the page table mapping it */
242 PageTableFrameNumber
= Pfn1
->u4
.PteFrame
;
243 Pfn2
= MiGetPfnEntry(PageTableFrameNumber
);
245 /* Remove a shared reference, since the page is going away */
246 MiDecrementShareCount(Pfn2
, PageTableFrameNumber
);
248 /* Set the special pending delete marker */
249 MI_SET_PFN_DELETED(Pfn1
);
251 /* And now delete the actual stack page */
252 MiDecrementShareCount(Pfn1
, PageFrameNumber
);
262 // We should be at the guard page now
264 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
266 /* Release the PFN lock */
267 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
272 MiReleaseSystemPtes(PointerPte
, StackPages
+ 1, SystemPteSpace
);
277 MmCreateKernelStack(IN BOOLEAN GuiStack
,
280 PFN_COUNT StackPtes
, StackPages
;
281 PMMPTE PointerPte
, StackPte
;
283 MMPTE TempPte
, InvalidPte
;
285 PFN_NUMBER PageFrameIndex
;
290 // Calculate pages needed
295 // We'll allocate 64KB stack, but only commit 12K
297 StackPtes
= BYTES_TO_PAGES(KERNEL_LARGE_STACK_SIZE
);
298 StackPages
= BYTES_TO_PAGES(KERNEL_LARGE_STACK_COMMIT
);
303 // If the dead stack S-LIST has a stack on it, use it instead of allocating
304 // new system PTEs for this stack
306 if (ExQueryDepthSList(&MmDeadStackSListHead
))
308 Pfn1
= (PMMPFN
)InterlockedPopEntrySList(&MmDeadStackSListHead
);
311 PointerPte
= Pfn1
->PteAddress
;
312 BaseAddress
= MiPteToAddress(++PointerPte
);
318 // We'll allocate 12K and that's it
320 StackPtes
= BYTES_TO_PAGES(KERNEL_STACK_SIZE
);
321 StackPages
= StackPtes
;
325 // Reserve stack pages, plus a guard page
327 StackPte
= MiReserveSystemPtes(StackPtes
+ 1, SystemPteSpace
);
328 if (!StackPte
) return NULL
;
331 // Get the stack address
333 BaseAddress
= MiPteToAddress(StackPte
+ StackPtes
+ 1);
336 // Select the right PTE address where we actually start committing pages
338 PointerPte
= StackPte
;
339 if (GuiStack
) PointerPte
+= BYTES_TO_PAGES(KERNEL_LARGE_STACK_SIZE
-
340 KERNEL_LARGE_STACK_COMMIT
);
343 /* Setup the temporary invalid PTE */
344 MI_MAKE_SOFTWARE_PTE(&InvalidPte
, MM_NOACCESS
);
346 /* Setup the template stack PTE */
347 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte
, PointerPte
+ 1, MM_READWRITE
, 0);
350 // Acquire the PFN DB lock
352 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
355 // Loop each stack page
357 for (i
= 0; i
< StackPages
; i
++)
364 /* Get a page and write the current invalid PTE */
365 MI_SET_USAGE(MI_USAGE_KERNEL_STACK
);
366 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName
);
367 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
368 MI_WRITE_INVALID_PTE(PointerPte
, InvalidPte
);
370 /* Initialize the PFN entry for this page */
371 MiInitializePfn(PageFrameIndex
, PointerPte
, 1);
373 /* Write the valid PTE */
374 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameIndex
;
375 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
379 // Release the PFN lock
381 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
384 // Return the stack address
391 MmGrowKernelStackEx(IN PVOID StackPointer
,
394 PKTHREAD Thread
= KeGetCurrentThread();
395 PMMPTE LimitPte
, NewLimitPte
, LastPte
;
397 MMPTE TempPte
, InvalidPte
;
398 PFN_NUMBER PageFrameIndex
;
401 // Make sure the stack did not overflow
403 ASSERT(((ULONG_PTR
)Thread
->StackBase
- (ULONG_PTR
)Thread
->StackLimit
) <=
404 (KERNEL_LARGE_STACK_SIZE
+ PAGE_SIZE
));
407 // Get the current stack limit
409 LimitPte
= MiAddressToPte(Thread
->StackLimit
);
410 ASSERT(LimitPte
->u
.Hard
.Valid
== 1);
413 // Get the new one and make sure this isn't a retarded request
415 NewLimitPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)StackPointer
- GrowSize
));
416 if (NewLimitPte
== LimitPte
) return STATUS_SUCCESS
;
419 // Now make sure you're not going past the reserved space
421 LastPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)Thread
->StackBase
-
422 KERNEL_LARGE_STACK_SIZE
));
423 if (NewLimitPte
< LastPte
)
428 DPRINT1("Thread wants too much stack\n");
429 return STATUS_STACK_OVERFLOW
;
433 // Calculate the number of new pages
437 /* Setup the temporary invalid PTE */
438 MI_MAKE_SOFTWARE_PTE(&InvalidPte
, MM_NOACCESS
);
441 // Acquire the PFN DB lock
443 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
446 // Loop each stack page
448 while (LimitPte
>= NewLimitPte
)
450 /* Get a page and write the current invalid PTE */
451 MI_SET_USAGE(MI_USAGE_KERNEL_STACK_EXPANSION
);
452 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName
);
453 PageFrameIndex
= MiRemoveAnyPage(MI_GET_NEXT_COLOR());
454 MI_WRITE_INVALID_PTE(LimitPte
, InvalidPte
);
456 /* Initialize the PFN entry for this page */
457 MiInitializePfn(PageFrameIndex
, LimitPte
, 1);
459 /* Setup the template stack PTE */
460 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte
, LimitPte
, MM_READWRITE
, PageFrameIndex
);
462 /* Write the valid PTE */
463 MI_WRITE_VALID_PTE(LimitPte
--, TempPte
);
467 // Release the PFN lock
469 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
474 Thread
->StackLimit
= (ULONG_PTR
)MiPteToAddress(NewLimitPte
);
475 return STATUS_SUCCESS
;
480 MmGrowKernelStack(IN PVOID StackPointer
)
483 // Call the extended version
485 return MmGrowKernelStackEx(StackPointer
, KERNEL_LARGE_STACK_COMMIT
);
490 MmSetMemoryPriorityProcess(IN PEPROCESS Process
,
491 IN UCHAR MemoryPriority
)
496 // Check if we have less then 16MB of Physical Memory
498 if ((MmSystemSize
== MmSmallSystem
) &&
499 (MmNumberOfPhysicalPages
< ((15 * 1024 * 1024) / PAGE_SIZE
)))
502 // Always use background priority
504 MemoryPriority
= MEMORY_PRIORITY_BACKGROUND
;
508 // Save the old priority and update it
510 OldPriority
= (UCHAR
)Process
->Vm
.Flags
.MemoryPriority
;
511 Process
->Vm
.Flags
.MemoryPriority
= MemoryPriority
;
514 // Return the old priority
521 MmCreatePeb(IN PEPROCESS Process
,
522 IN PINITIAL_PEB InitialPeb
,
526 LARGE_INTEGER SectionOffset
;
528 PVOID TableBase
= NULL
;
529 PIMAGE_NT_HEADERS NtHeaders
;
530 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData
;
532 USHORT Characteristics
;
533 KAFFINITY ProcessAffinityMask
= 0;
534 SectionOffset
.QuadPart
= (ULONGLONG
)0;
540 KeAttachProcess(&Process
->Pcb
);
545 Status
= MmMapViewOfSection(ExpNlsSectionPointer
,
555 DPRINT("NLS Tables at: %p\n", TableBase
);
556 if (!NT_SUCCESS(Status
))
558 /* Cleanup and exit */
566 Status
= MiCreatePebOrTeb(Process
, sizeof(PEB
), (PULONG_PTR
)&Peb
);
567 DPRINT("PEB at: %p\n", Peb
);
568 if (!NT_SUCCESS(Status
))
570 /* Cleanup and exit */
576 // Use SEH in case we can't load the PEB
581 // Initialize the PEB
583 RtlZeroMemory(Peb
, sizeof(PEB
));
588 Peb
->ImageBaseAddress
= Process
->SectionBaseAddress
;
589 Peb
->InheritedAddressSpace
= InitialPeb
->InheritedAddressSpace
;
590 Peb
->Mutant
= InitialPeb
->Mutant
;
591 Peb
->ImageUsesLargePages
= InitialPeb
->ImageUsesLargePages
;
596 Peb
->AnsiCodePageData
= (PCHAR
)TableBase
+ ExpAnsiCodePageDataOffset
;
597 Peb
->OemCodePageData
= (PCHAR
)TableBase
+ ExpOemCodePageDataOffset
;
598 Peb
->UnicodeCaseTableData
= (PCHAR
)TableBase
+ ExpUnicodeCaseTableDataOffset
;
601 // Default Version Data (could get changed below)
603 Peb
->OSMajorVersion
= NtMajorVersion
;
604 Peb
->OSMinorVersion
= NtMinorVersion
;
605 Peb
->OSBuildNumber
= (USHORT
)(NtBuildNumber
& 0x3FFF);
606 Peb
->OSPlatformId
= VER_PLATFORM_WIN32_NT
;
607 Peb
->OSCSDVersion
= (USHORT
)CmNtCSDVersion
;
610 // Heap and Debug Data
612 Peb
->NumberOfProcessors
= KeNumberProcessors
;
613 Peb
->BeingDebugged
= (BOOLEAN
)(Process
->DebugPort
!= NULL
);
614 Peb
->NtGlobalFlag
= NtGlobalFlag
;
615 Peb
->HeapSegmentReserve
= MmHeapSegmentReserve
;
616 Peb
->HeapSegmentCommit
= MmHeapSegmentCommit
;
617 Peb
->HeapDeCommitTotalFreeThreshold
= MmHeapDeCommitTotalFreeThreshold
;
618 Peb
->HeapDeCommitFreeBlockThreshold
= MmHeapDeCommitFreeBlockThreshold
;
619 Peb
->CriticalSectionTimeout
= MmCriticalSectionTimeout
;
620 Peb
->MinimumStackCommit
= MmMinimumStackCommitInBytes
;
621 Peb
->MaximumNumberOfHeaps
= (PAGE_SIZE
- sizeof(PEB
)) / sizeof(PVOID
);
622 Peb
->ProcessHeaps
= (PVOID
*)(Peb
+ 1);
627 if (Process
->Session
) Peb
->SessionId
= MmGetSessionId(Process
);
629 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
635 _SEH2_YIELD(return _SEH2_GetExceptionCode());
640 // Use SEH in case we can't load the image
647 NtHeaders
= RtlImageNtHeader(Peb
->ImageBaseAddress
);
648 Characteristics
= NtHeaders
->FileHeader
.Characteristics
;
650 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
656 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT
);
666 // Use SEH in case we can't load the headers
671 // Get the Image Config Data too
673 ImageConfigData
= RtlImageDirectoryEntryToData(Peb
->ImageBaseAddress
,
675 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG
,
682 ProbeForRead(ImageConfigData
,
683 sizeof(IMAGE_LOAD_CONFIG_DIRECTORY
),
688 // Write subsystem data
690 Peb
->ImageSubsystem
= NtHeaders
->OptionalHeader
.Subsystem
;
691 Peb
->ImageSubsystemMajorVersion
= NtHeaders
->OptionalHeader
.MajorSubsystemVersion
;
692 Peb
->ImageSubsystemMinorVersion
= NtHeaders
->OptionalHeader
.MinorSubsystemVersion
;
695 // Check for version data
697 if (NtHeaders
->OptionalHeader
.Win32VersionValue
)
700 // Extract values and write them
702 Peb
->OSMajorVersion
= NtHeaders
->OptionalHeader
.Win32VersionValue
& 0xFF;
703 Peb
->OSMinorVersion
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 8) & 0xFF;
704 Peb
->OSBuildNumber
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 16) & 0x3FFF;
705 Peb
->OSPlatformId
= (NtHeaders
->OptionalHeader
.Win32VersionValue
>> 30) ^ 2;
707 /* Process CSD version override */
708 if ((ImageConfigData
) && (ImageConfigData
->CSDVersion
))
710 /* Take the value from the image configuration directory */
711 Peb
->OSCSDVersion
= ImageConfigData
->CSDVersion
;
715 /* Process optional process affinity mask override */
716 if ((ImageConfigData
) && (ImageConfigData
->ProcessAffinityMask
))
718 /* Take the value from the image configuration directory */
719 ProcessAffinityMask
= ImageConfigData
->ProcessAffinityMask
;
723 // Check if this is a UP image
724 if (Characteristics
& IMAGE_FILE_UP_SYSTEM_ONLY
)
727 // Force it to use CPU 0
729 /* FIXME: this should use the MmRotatingUniprocessorNumber */
730 Peb
->ImageProcessAffinityMask
= 0;
735 // Whatever was configured
737 Peb
->ImageProcessAffinityMask
= ProcessAffinityMask
;
740 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
746 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT
);
752 // Detach from the Process
756 return STATUS_SUCCESS
;
761 MmCreateTeb(IN PEPROCESS Process
,
762 IN PCLIENT_ID ClientId
,
763 IN PINITIAL_TEB InitialTeb
,
767 NTSTATUS Status
= STATUS_SUCCESS
;
773 KeAttachProcess(&Process
->Pcb
);
778 Status
= MiCreatePebOrTeb(Process
, sizeof(TEB
), (PULONG_PTR
)&Teb
);
779 ASSERT(NT_SUCCESS(Status
));
782 // Use SEH in case we can't load the TEB
787 // Initialize the PEB
789 RtlZeroMemory(Teb
, sizeof(TEB
));
795 Teb
->NtTib
.ExceptionList
= NULL
;
797 Teb
->NtTib
.ExceptionList
= EXCEPTION_CHAIN_END
;
799 Teb
->NtTib
.Self
= (PNT_TIB
)Teb
;
802 // Identify this as an OS/2 V3.0 ("Cruiser") TIB
804 Teb
->NtTib
.Version
= 30 << 8;
809 Teb
->ClientId
= *ClientId
;
810 Teb
->RealClientId
= *ClientId
;
811 Teb
->ProcessEnvironmentBlock
= Process
->Peb
;
812 Teb
->CurrentLocale
= PsDefaultThreadLocaleId
;
815 // Check if we have a grandparent TEB
817 if ((InitialTeb
->PreviousStackBase
== NULL
) &&
818 (InitialTeb
->PreviousStackLimit
== NULL
))
821 // Use initial TEB values
823 Teb
->NtTib
.StackBase
= InitialTeb
->StackBase
;
824 Teb
->NtTib
.StackLimit
= InitialTeb
->StackLimit
;
825 Teb
->DeallocationStack
= InitialTeb
->AllocatedStackBase
;
830 // Use grandparent TEB values
832 Teb
->NtTib
.StackBase
= InitialTeb
->PreviousStackBase
;
833 Teb
->NtTib
.StackLimit
= InitialTeb
->PreviousStackLimit
;
837 // Initialize the static unicode string
839 Teb
->StaticUnicodeString
.MaximumLength
= sizeof(Teb
->StaticUnicodeBuffer
);
840 Teb
->StaticUnicodeString
.Buffer
= Teb
->StaticUnicodeBuffer
;
842 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
847 Status
= _SEH2_GetExceptionCode();
861 MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess
)
867 /* Setup some bogus list data */
868 MmWorkingSetList
->LastEntry
= CurrentProcess
->Vm
.MinimumWorkingSetSize
;
869 MmWorkingSetList
->HashTable
= NULL
;
870 MmWorkingSetList
->HashTableSize
= 0;
871 MmWorkingSetList
->NumberOfImageWaiters
= 0;
872 MmWorkingSetList
->Wsle
= (PVOID
)0xDEADBABE;
873 MmWorkingSetList
->VadBitMapHint
= 1;
874 MmWorkingSetList
->HashTableStart
= (PVOID
)0xBADAB00B;
875 MmWorkingSetList
->HighestPermittedHashAddress
= (PVOID
)0xCAFEBABE;
876 MmWorkingSetList
->FirstFree
= 1;
877 MmWorkingSetList
->FirstDynamic
= 2;
878 MmWorkingSetList
->NextSlot
= 3;
879 MmWorkingSetList
->LastInitializedWsle
= 4;
881 /* The rule is that the owner process is always in the FLINK of the PDE's PFN entry */
882 Pfn1
= MiGetPfnEntry(CurrentProcess
->Pcb
.DirectoryTableBase
[0] >> PAGE_SHIFT
);
883 ASSERT(Pfn1
->u4
.PteFrame
== MiGetPfnEntryIndex(Pfn1
));
884 Pfn1
->u1
.Event
= (PKEVENT
)CurrentProcess
;
886 /* Map the process working set in kernel space */
887 sysPte
= MiReserveSystemPtes(1, SystemPteSpace
);
888 MI_MAKE_HARDWARE_PTE_KERNEL(&tempPte
, sysPte
, MM_READWRITE
, CurrentProcess
->WorkingSetPage
);
889 MI_WRITE_VALID_PTE(sysPte
, tempPte
);
890 CurrentProcess
->Vm
.VmWorkingSetList
= MiPteToAddress(sysPte
);
895 MmInitializeProcessAddressSpace(IN PEPROCESS Process
,
896 IN PEPROCESS ProcessClone OPTIONAL
,
897 IN PVOID Section OPTIONAL
,
899 IN POBJECT_NAME_INFORMATION
*AuditName OPTIONAL
)
901 NTSTATUS Status
= STATUS_SUCCESS
;
904 PROS_SECTION_OBJECT SectionObject
= Section
;
908 PFN_NUMBER PageFrameNumber
;
909 UNICODE_STRING FileName
;
915 /* We should have a PDE */
916 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] != 0);
917 ASSERT(Process
->PdeUpdateNeeded
== FALSE
);
919 /* Attach to the process */
920 KeAttachProcess(&Process
->Pcb
);
922 /* The address space should now been in phase 1 or 0 */
923 ASSERT(Process
->AddressSpaceInitialized
<= 1);
924 Process
->AddressSpaceInitialized
= 2;
926 /* Initialize the Addresss Space lock */
927 KeInitializeGuardedMutex(&Process
->AddressCreationLock
);
928 Process
->Vm
.WorkingSetExpansionLinks
.Flink
= NULL
;
930 /* Initialize AVL tree */
931 ASSERT(Process
->VadRoot
.NumberGenericTableElements
== 0);
932 Process
->VadRoot
.BalancedRoot
.u1
.Parent
= &Process
->VadRoot
.BalancedRoot
;
934 /* Lock PFN database */
935 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
937 /* Setup the PFN for the PDE base of this process */
939 PointerPte
= MiAddressToPte(PXE_BASE
);
941 PointerPte
= MiAddressToPte(PDE_BASE
);
943 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
944 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] == PageFrameNumber
* PAGE_SIZE
);
945 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
947 /* Do the same for hyperspace */
949 PointerPde
= MiAddressToPxe((PVOID
)HYPER_SPACE
);
951 PointerPde
= MiAddressToPde(HYPER_SPACE
);
953 PageFrameNumber
= PFN_FROM_PTE(PointerPde
);
954 //ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE); // we're not lucky
955 MiInitializePfn(PageFrameNumber
, (PMMPTE
)PointerPde
, TRUE
);
957 /* Setup the PFN for the PTE for the working set */
958 PointerPte
= MiAddressToPte(MI_WORKING_SET_LIST
);
959 MI_MAKE_HARDWARE_PTE(&TempPte
, PointerPte
, MM_READWRITE
, 0);
960 ASSERT(PointerPte
->u
.Long
!= 0);
961 PageFrameNumber
= PFN_FROM_PTE(PointerPte
);
962 MI_WRITE_INVALID_PTE(PointerPte
, DemandZeroPte
);
963 MiInitializePfn(PageFrameNumber
, PointerPte
, TRUE
);
964 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
965 MI_WRITE_VALID_PTE(PointerPte
, TempPte
);
967 /* Now initialize the working set list */
968 MiInitializeWorkingSetList(Process
);
971 ASSERT(Process
->PhysicalVadRoot
== NULL
);
973 /* Release PFN lock */
974 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
976 /* Lock the VAD, ARM3-owned ranges away */
977 MiRosTakeOverSharedUserPage(Process
);
979 /* Check if there's a Section Object */
982 /* Determine the image file name and save it to EPROCESS */
983 FileName
= SectionObject
->FileObject
->FileName
;
984 Source
= (PWCHAR
)((PCHAR
)FileName
.Buffer
+ FileName
.Length
);
987 /* Loop the file name*/
988 while (Source
> FileName
.Buffer
)
990 /* Make sure this isn't a backslash */
991 if (*--Source
== OBJ_NAME_PATH_SEPARATOR
)
993 /* If so, stop it here */
999 /* Otherwise, keep going */
1005 /* Copy the to the process and truncate it to 15 characters if necessary */
1006 Destination
= Process
->ImageFileName
;
1007 Length
= min(Length
, sizeof(Process
->ImageFileName
) - 1);
1008 while (Length
--) *Destination
++ = (UCHAR
)*Source
++;
1009 *Destination
= ANSI_NULL
;
1011 /* Check if caller wants an audit name */
1014 /* Setup the audit name */
1015 Status
= SeInitializeProcessAuditName(SectionObject
->FileObject
,
1018 if (!NT_SUCCESS(Status
))
1026 /* Map the section */
1027 Status
= MmMapViewOfSection(Section
,
1038 /* Save the pointer */
1039 Process
->SectionBaseAddress
= ImageBase
;
1042 /* Be nice and detach */
1045 /* Return status to caller */
1052 MmInitializeHandBuiltProcess(IN PEPROCESS Process
,
1053 IN PULONG_PTR DirectoryTableBase
)
1055 /* Share the directory base with the idle process */
1056 DirectoryTableBase
[0] = PsGetCurrentProcess()->Pcb
.DirectoryTableBase
[0];
1057 DirectoryTableBase
[1] = PsGetCurrentProcess()->Pcb
.DirectoryTableBase
[1];
1059 /* Initialize the Addresss Space */
1060 KeInitializeGuardedMutex(&Process
->AddressCreationLock
);
1061 KeInitializeSpinLock(&Process
->HyperSpaceLock
);
1062 Process
->Vm
.WorkingSetExpansionLinks
.Flink
= NULL
;
1063 ASSERT(Process
->VadRoot
.NumberGenericTableElements
== 0);
1064 Process
->VadRoot
.BalancedRoot
.u1
.Parent
= &Process
->VadRoot
.BalancedRoot
;
1066 /* Use idle process Working set */
1067 Process
->Vm
.VmWorkingSetList
= PsGetCurrentProcess()->Vm
.VmWorkingSetList
;
1070 Process
->HasAddressSpace
= TRUE
;//??
1071 return STATUS_SUCCESS
;
1077 MmInitializeHandBuiltProcess2(IN PEPROCESS Process
)
1079 /* Lock the VAD, ARM3-owned ranges away */
1080 MiRosTakeOverSharedUserPage(Process
);
1081 return STATUS_SUCCESS
;
1085 /* FIXME: Evaluate ways to make this portable yet arch-specific */
1088 MmCreateProcessAddressSpace(IN ULONG MinWs
,
1089 IN PEPROCESS Process
,
1090 OUT PULONG_PTR DirectoryTableBase
)
1093 PFN_NUMBER PdeIndex
, HyperIndex
, WsListIndex
;
1095 MMPTE TempPte
, PdePte
;
1097 PMMPTE SystemTable
, HyperTable
;
1101 /* Choose a process color */
1102 Process
->NextPageColor
= (USHORT
)RtlRandom(&MmProcessColorSeed
);
1104 /* Setup the hyperspace lock */
1105 KeInitializeSpinLock(&Process
->HyperSpaceLock
);
1107 /* Lock PFN database */
1108 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1110 /* Get a zero page for the PDE, if possible */
1111 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1112 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY
);
1113 PdeIndex
= MiRemoveZeroPageSafe(Color
);
1116 /* No zero pages, grab a free one */
1117 PdeIndex
= MiRemoveAnyPage(Color
);
1119 /* Zero it outside the PFN lock */
1120 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1121 MiZeroPhysicalPage(PdeIndex
);
1122 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1125 /* Get a zero page for hyperspace, if possible */
1126 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY
);
1127 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1128 HyperIndex
= MiRemoveZeroPageSafe(Color
);
1131 /* No zero pages, grab a free one */
1132 HyperIndex
= MiRemoveAnyPage(Color
);
1134 /* Zero it outside the PFN lock */
1135 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1136 MiZeroPhysicalPage(HyperIndex
);
1137 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1140 /* Get a zero page for the woring set list, if possible */
1141 MI_SET_USAGE(MI_USAGE_PAGE_TABLE
);
1142 Color
= MI_GET_NEXT_PROCESS_COLOR(Process
);
1143 WsListIndex
= MiRemoveZeroPageSafe(Color
);
1146 /* No zero pages, grab a free one */
1147 WsListIndex
= MiRemoveAnyPage(Color
);
1149 /* Zero it outside the PFN lock */
1150 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1151 MiZeroPhysicalPage(WsListIndex
);
1155 /* Release the PFN lock */
1156 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1159 /* Switch to phase 1 initialization */
1160 ASSERT(Process
->AddressSpaceInitialized
== 0);
1161 Process
->AddressSpaceInitialized
= 1;
1163 /* Set the base directory pointers */
1164 Process
->WorkingSetPage
= WsListIndex
;
1165 DirectoryTableBase
[0] = PdeIndex
<< PAGE_SHIFT
;
1166 DirectoryTableBase
[1] = HyperIndex
<< PAGE_SHIFT
;
1168 /* Make sure we don't already have a page directory setup */
1169 ASSERT(Process
->Pcb
.DirectoryTableBase
[0] == 0);
1171 /* Get a PTE to map hyperspace */
1172 PointerPte
= MiReserveSystemPtes(1, SystemPteSpace
);
1173 ASSERT(PointerPte
!= NULL
);
1176 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte
,
1181 /* Set it dirty and map it */
1182 MI_MAKE_DIRTY_PAGE(&PdePte
);
1183 MI_WRITE_VALID_PTE(PointerPte
, PdePte
);
1185 /* Now get hyperspace's page table */
1186 HyperTable
= MiPteToAddress(PointerPte
);
1188 /* Now write the PTE/PDE entry for the working set list index itself */
1189 TempPte
= ValidKernelPte
;
1190 TempPte
.u
.Hard
.PageFrameNumber
= WsListIndex
;
1191 /* Hyperspace is local */
1192 MI_MAKE_LOCAL_PAGE(&TempPte
);
1193 PdeOffset
= MiAddressToPteOffset(MmWorkingSetList
);
1194 HyperTable
[PdeOffset
] = TempPte
;
1196 /* Let go of the system PTE */
1197 MiReleaseSystemPtes(PointerPte
, 1, SystemPteSpace
);
1199 /* Save the PTE address of the page directory itself */
1200 Pfn1
= MiGetPfnEntry(PdeIndex
);
1201 Pfn1
->PteAddress
= (PMMPTE
)PDE_BASE
;
1203 /* Insert us into the Mm process list */
1204 InsertTailList(&MmProcessList
, &Process
->MmProcessLinks
);
1206 /* Get a PTE to map the page directory */
1207 PointerPte
= MiReserveSystemPtes(1, SystemPteSpace
);
1208 ASSERT(PointerPte
!= NULL
);
1211 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte
,
1216 /* Set it dirty and map it */
1217 MI_MAKE_DIRTY_PAGE(&PdePte
);
1218 MI_WRITE_VALID_PTE(PointerPte
, PdePte
);
1220 /* Now get the page directory (which we'll double map, so call it a page table */
1221 SystemTable
= MiPteToAddress(PointerPte
);
1223 /* Copy all the kernel mappings */
1224 PdeOffset
= MiGetPdeOffset(MmSystemRangeStart
);
1225 RtlCopyMemory(&SystemTable
[PdeOffset
],
1226 MiAddressToPde(MmSystemRangeStart
),
1227 PAGE_SIZE
- PdeOffset
* sizeof(MMPTE
));
1229 /* Now write the PTE/PDE entry for hyperspace itself */
1230 TempPte
= ValidKernelPte
;
1231 TempPte
.u
.Hard
.PageFrameNumber
= HyperIndex
;
1232 PdeOffset
= MiGetPdeOffset(HYPER_SPACE
);
1233 SystemTable
[PdeOffset
] = TempPte
;
1237 ASSERT(MiGetPdeOffset(MmHyperSpaceEnd
) >= PdeOffset
);
1239 /* Now do the x86 trick of making the PDE a page table itself */
1240 PdeOffset
= MiGetPdeOffset(PTE_BASE
);
1241 TempPte
.u
.Hard
.PageFrameNumber
= PdeIndex
;
1242 SystemTable
[PdeOffset
] = TempPte
;
1244 /* Let go of the system PTE */
1245 MiReleaseSystemPtes(PointerPte
, 1, SystemPteSpace
);
1247 /* Add the process to the session */
1248 MiSessionAddProcess(Process
);
1255 MmCleanProcessAddressSpace(IN PEPROCESS Process
)
1258 PMM_AVL_TABLE VadTree
;
1259 PETHREAD Thread
= PsGetCurrentThread();
1261 /* Only support this */
1262 ASSERT(Process
->AddressSpaceInitialized
== 2);
1264 /* Remove from the session */
1265 MiSessionRemoveProcess();
1267 /* Lock the process address space from changes */
1268 MmLockAddressSpace(&Process
->Vm
);
1269 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1271 /* VM is deleted now */
1272 Process
->VmDeleted
= TRUE
;
1273 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1275 /* Enumerate the VADs */
1276 VadTree
= &Process
->VadRoot
;
1277 while (VadTree
->NumberGenericTableElements
)
1279 /* Grab the current VAD */
1280 Vad
= (PMMVAD
)VadTree
->BalancedRoot
.RightChild
;
1282 /* Check for old-style memory areas */
1283 if (Vad
->u
.VadFlags
.Spare
== 1)
1285 /* Let RosMm handle this */
1286 MiRosCleanupMemoryArea(Process
, Vad
);
1290 /* Lock the working set */
1291 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1293 /* Remove this VAD from the tree */
1294 ASSERT(VadTree
->NumberGenericTableElements
>= 1);
1295 MiRemoveNode((PMMADDRESS_NODE
)Vad
, VadTree
);
1297 /* Only regular VADs supported for now */
1298 ASSERT(Vad
->u
.VadFlags
.VadType
== VadNone
);
1300 /* Check if this is a section VAD */
1301 if (!(Vad
->u
.VadFlags
.PrivateMemory
) && (Vad
->ControlArea
))
1303 /* Remove the view */
1304 MiRemoveMappedView(Process
, Vad
);
1308 /* Delete the addresses */
1309 MiDeleteVirtualAddresses(Vad
->StartingVpn
<< PAGE_SHIFT
,
1310 (Vad
->EndingVpn
<< PAGE_SHIFT
) | (PAGE_SIZE
- 1),
1313 /* Release the working set */
1314 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1317 /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */
1318 if (Vad
->u
.VadFlags
.Spare
== 1)
1320 /* Set a flag so MmDeleteMemoryArea knows to free, but not to remove */
1321 Vad
->u
.VadFlags
.Spare
= 2;
1325 /* Free the VAD memory */
1329 /* Lock the working set */
1330 MiLockProcessWorkingSetUnsafe(Process
, Thread
);
1331 ASSERT(Process
->CloneRoot
== NULL
);
1332 ASSERT(Process
->PhysicalVadRoot
== NULL
);
1334 /* Delete the shared user data section */
1335 MiDeleteVirtualAddresses(USER_SHARED_DATA
, USER_SHARED_DATA
, NULL
);
1337 /* Release the working set */
1338 MiUnlockProcessWorkingSetUnsafe(Process
, Thread
);
1340 /* Release the address space */
1341 MmUnlockAddressSpace(&Process
->Vm
);
1346 MmDeleteProcessAddressSpace2(IN PEPROCESS Process
)
1350 PFN_NUMBER PageFrameIndex
;
1352 //ASSERT(Process->CommitCharge == 0);
1354 /* Acquire the PFN lock */
1355 OldIrql
= KeAcquireQueuedSpinLock(LockQueuePfnLock
);
1357 /* Check for fully initialized process */
1358 if (Process
->AddressSpaceInitialized
== 2)
1360 /* Map the working set page and its page table */
1361 Pfn1
= MiGetPfnEntry(Process
->WorkingSetPage
);
1362 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1365 MI_SET_PFN_DELETED(Pfn1
);
1366 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1367 MiDecrementShareCount(Pfn1
, Process
->WorkingSetPage
);
1368 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1369 MiReleaseSystemPtes(MiAddressToPte(Process
->Vm
.VmWorkingSetList
), 1, SystemPteSpace
);
1371 /* Now map hyperspace and its page table */
1372 PageFrameIndex
= Process
->Pcb
.DirectoryTableBase
[1] >> PAGE_SHIFT
;
1373 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1374 Pfn2
= MiGetPfnEntry(Pfn1
->u4
.PteFrame
);
1377 MI_SET_PFN_DELETED(Pfn1
);
1378 MiDecrementShareCount(Pfn2
, Pfn1
->u4
.PteFrame
);
1379 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1380 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1382 /* Finally, nuke the PDE itself */
1383 PageFrameIndex
= Process
->Pcb
.DirectoryTableBase
[0] >> PAGE_SHIFT
;
1384 Pfn1
= MiGetPfnEntry(PageFrameIndex
);
1385 MI_SET_PFN_DELETED(Pfn1
);
1386 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1387 MiDecrementShareCount(Pfn1
, PageFrameIndex
);
1389 /* Page table is now dead. Bye bye... */
1390 ASSERT((Pfn1
->u3
.e2
.ReferenceCount
== 0) || (Pfn1
->u3
.e1
.WriteInProgress
));
1394 /* A partly-initialized process should never exit through here */
1398 /* Release the PFN lock */
1399 KeReleaseQueuedSpinLock(LockQueuePfnLock
, OldIrql
);
1401 /* Drop a reference on the session */
1402 if (Process
->Session
) MiReleaseProcessReferenceToSessionDataPage(Process
->Session
);
1404 /* Clear out the PDE pages */
1405 Process
->Pcb
.DirectoryTableBase
[0] = 0;
1406 Process
->Pcb
.DirectoryTableBase
[1] = 0;
1410 /* SYSTEM CALLS ***************************************************************/
1414 NtAllocateUserPhysicalPages(IN HANDLE ProcessHandle
,
1415 IN OUT PULONG_PTR NumberOfPages
,
1416 IN OUT PULONG_PTR UserPfnArray
)
1419 return STATUS_NOT_IMPLEMENTED
;
1424 NtMapUserPhysicalPages(IN PVOID VirtualAddresses
,
1425 IN ULONG_PTR NumberOfPages
,
1426 IN OUT PULONG_PTR UserPfnArray
)
1429 return STATUS_NOT_IMPLEMENTED
;
1434 NtMapUserPhysicalPagesScatter(IN PVOID
*VirtualAddresses
,
1435 IN ULONG_PTR NumberOfPages
,
1436 IN OUT PULONG_PTR UserPfnArray
)
1439 return STATUS_NOT_IMPLEMENTED
;
1444 NtFreeUserPhysicalPages(IN HANDLE ProcessHandle
,
1445 IN OUT PULONG_PTR NumberOfPages
,
1446 IN OUT PULONG_PTR UserPfnArray
)
1449 return STATUS_NOT_IMPLEMENTED
;