[NTOSKRNL]: Implement support for session pool (not yet enabled) and session space...
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / procsup.c
1 /*
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
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
17
18 /* GLOBALS ********************************************************************/
19
20 ULONG MmProcessColorSeed = 0x12345678;
21 PMMWSL MmWorkingSetList;
22 ULONG MmMaximumDeadKernelStacks = 5;
23 SLIST_HEADER MmDeadStackSListHead;
24
25 /* PRIVATE FUNCTIONS **********************************************************/
26
27 VOID
28 NTAPI
29 MiRosTakeOverSharedUserPage(IN PEPROCESS Process)
30 {
31 NTSTATUS Status;
32 PMEMORY_AREA MemoryArea;
33 PHYSICAL_ADDRESS BoundaryAddressMultiple;
34 PVOID AllocatedBase = (PVOID)MM_SHARED_USER_DATA_VA;
35 BoundaryAddressMultiple.QuadPart = 0;
36
37 Status = MmCreateMemoryArea(&Process->Vm,
38 MEMORY_AREA_OWNED_BY_ARM3,
39 &AllocatedBase,
40 PAGE_SIZE,
41 PAGE_READWRITE,
42 &MemoryArea,
43 TRUE,
44 0,
45 BoundaryAddressMultiple);
46 ASSERT(NT_SUCCESS(Status));
47 }
48
49 NTSTATUS
50 NTAPI
51 MiCreatePebOrTeb(IN PEPROCESS Process,
52 IN ULONG Size,
53 OUT PULONG_PTR Base)
54 {
55 PETHREAD Thread = PsGetCurrentThread();
56 PMMVAD_LONG Vad;
57 NTSTATUS Status;
58 ULONG RandomCoeff;
59 ULONG_PTR StartAddress, EndAddress;
60 LARGE_INTEGER CurrentTime;
61 TABLE_SEARCH_RESULT Result = TableFoundNode;
62 PMMADDRESS_NODE Parent;
63
64 /* Allocate a VAD */
65 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
66 if (!Vad) return STATUS_NO_MEMORY;
67
68 /* Setup the primary flags with the size, and make it commited, private, RW */
69 Vad->u.LongFlags = 0;
70 Vad->u.VadFlags.CommitCharge = BYTES_TO_PAGES(Size);
71 Vad->u.VadFlags.MemCommit = TRUE;
72 Vad->u.VadFlags.PrivateMemory = TRUE;
73 Vad->u.VadFlags.Protection = MM_READWRITE;
74 Vad->u.VadFlags.NoChange = TRUE;
75
76 /* Setup the secondary flags to make it a secured, writable, long VAD */
77 Vad->u2.LongFlags2 = 0;
78 Vad->u2.VadFlags2.OneSecured = TRUE;
79 Vad->u2.VadFlags2.LongVad = TRUE;
80 Vad->u2.VadFlags2.ReadOnly = FALSE;
81
82 /* Lock the process address space */
83 KeAcquireGuardedMutex(&Process->AddressCreationLock);
84
85 /* Check if this is a PEB creation */
86 if (Size == sizeof(PEB))
87 {
88 /* Start at the highest valid address */
89 StartAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1;
90
91 /* Select the random coefficient */
92 KeQueryTickCount(&CurrentTime);
93 CurrentTime.LowPart &= ((64 * _1KB) >> PAGE_SHIFT) - 1;
94 if (CurrentTime.LowPart <= 1) CurrentTime.LowPart = 2;
95 RandomCoeff = CurrentTime.LowPart << PAGE_SHIFT;
96
97 /* Select the highest valid address minus the random coefficient */
98 StartAddress -= RandomCoeff;
99 EndAddress = StartAddress + ROUND_TO_PAGES(Size) - 1;
100
101 /* Try to find something below the random upper margin */
102 Result = MiFindEmptyAddressRangeDownTree(ROUND_TO_PAGES(Size),
103 EndAddress,
104 PAGE_SIZE,
105 &Process->VadRoot,
106 Base,
107 &Parent);
108 }
109
110 /* Check for success. TableFoundNode means nothing free. */
111 if (Result == TableFoundNode)
112 {
113 /* For TEBs, or if a PEB location couldn't be found, scan the VAD root */
114 Result = MiFindEmptyAddressRangeDownTree(ROUND_TO_PAGES(Size),
115 (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1,
116 PAGE_SIZE,
117 &Process->VadRoot,
118 Base,
119 &Parent);
120 /* Bail out, if still nothing free was found */
121 if (Result == TableFoundNode) return STATUS_NO_MEMORY;
122 }
123
124 /* Validate that it came from the VAD ranges */
125 ASSERT(*Base >= (ULONG_PTR)MI_LOWEST_VAD_ADDRESS);
126
127 /* Build the rest of the VAD now */
128 Vad->StartingVpn = (*Base) >> PAGE_SHIFT;
129 Vad->EndingVpn = ((*Base) + Size - 1) >> PAGE_SHIFT;
130 Vad->u3.Secured.StartVpn = *Base;
131 Vad->u3.Secured.EndVpn = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1);
132 Vad->u1.Parent = NULL;
133
134 /* FIXME: Should setup VAD bitmap */
135 Status = STATUS_SUCCESS;
136
137 /* Pretend as if we own the working set */
138 MiLockProcessWorkingSet(Process, Thread);
139
140 /* Insert the VAD */
141 ASSERT(Vad->EndingVpn >= Vad->StartingVpn);
142 Process->VadRoot.NodeHint = Vad;
143 Vad->ControlArea = NULL; // For Memory-Area hack
144 Vad->FirstPrototypePte = NULL;
145 DPRINT("VAD: %p\n", Vad);
146 DPRINT("Allocated PEB/TEB at: 0x%p for %16s\n", *Base, Process->ImageFileName);
147 MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
148
149 /* Release the working set */
150 MiUnlockProcessWorkingSet(Process, Thread);
151
152 /* Release the address space lock */
153 KeReleaseGuardedMutex(&Process->AddressCreationLock);
154
155 /* Return the status */
156 return Status;
157 }
158
159 VOID
160 NTAPI
161 MmDeleteTeb(IN PEPROCESS Process,
162 IN PTEB Teb)
163 {
164 ULONG_PTR TebEnd;
165 PETHREAD Thread = PsGetCurrentThread();
166 PMMVAD Vad;
167 PMM_AVL_TABLE VadTree = &Process->VadRoot;
168 DPRINT("Deleting TEB: %p in %16s\n", Teb, Process->ImageFileName);
169
170 /* TEB is one page */
171 TebEnd = (ULONG_PTR)Teb + ROUND_TO_PAGES(sizeof(TEB)) - 1;
172
173 /* Attach to the process */
174 KeAttachProcess(&Process->Pcb);
175
176 /* Lock the process address space */
177 KeAcquireGuardedMutex(&Process->AddressCreationLock);
178
179 /* Find the VAD, make sure it's a TEB VAD */
180 Vad = MiLocateAddress(Teb);
181 DPRINT("Removing node for VAD: %lx %lx\n", Vad->StartingVpn, Vad->EndingVpn);
182 ASSERT(Vad != NULL);
183 if (Vad->StartingVpn != ((ULONG_PTR)Teb >> PAGE_SHIFT))
184 {
185 /* Bug in the AVL code? */
186 DPRINT1("Corrupted VAD!\n");
187 }
188 else
189 {
190 /* Sanity checks for a valid TEB VAD */
191 ASSERT((Vad->StartingVpn == ((ULONG_PTR)Teb >> PAGE_SHIFT) &&
192 (Vad->EndingVpn == (TebEnd >> PAGE_SHIFT))));
193 ASSERT(Vad->u.VadFlags.NoChange == TRUE);
194 ASSERT(Vad->u2.VadFlags2.OneSecured == TRUE);
195 ASSERT(Vad->u2.VadFlags2.MultipleSecured == FALSE);
196
197 /* Lock the working set */
198 MiLockProcessWorkingSet(Process, Thread);
199
200 /* Remove this VAD from the tree */
201 ASSERT(VadTree->NumberGenericTableElements >= 1);
202 MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree);
203
204 /* Delete the pages */
205 MiDeleteVirtualAddresses((ULONG_PTR)Teb, TebEnd, NULL);
206
207 /* Release the working set */
208 MiUnlockProcessWorkingSet(Process, Thread);
209
210 /* Remove the VAD */
211 ExFreePool(Vad);
212 }
213
214 /* Release the address space lock */
215 KeReleaseGuardedMutex(&Process->AddressCreationLock);
216
217 /* Detach */
218 KeDetachProcess();
219 }
220
221 VOID
222 NTAPI
223 MmDeleteKernelStack(IN PVOID StackBase,
224 IN BOOLEAN GuiStack)
225 {
226 PMMPTE PointerPte;
227 PFN_NUMBER PageFrameNumber, PageTableFrameNumber;
228 PFN_COUNT StackPages;
229 PMMPFN Pfn1, Pfn2;
230 ULONG i;
231 KIRQL OldIrql;
232
233 //
234 // This should be the guard page, so decrement by one
235 //
236 PointerPte = MiAddressToPte(StackBase);
237 PointerPte--;
238
239 //
240 // If this is a small stack, just push the stack onto the dead stack S-LIST
241 //
242 if (!GuiStack)
243 {
244 if (ExQueryDepthSList(&MmDeadStackSListHead) < MmMaximumDeadKernelStacks)
245 {
246 Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
247 InterlockedPushEntrySList(&MmDeadStackSListHead,
248 (PSLIST_ENTRY)&Pfn1->u1.NextStackPfn);
249 return;
250 }
251 }
252
253 //
254 // Calculate pages used
255 //
256 StackPages = BYTES_TO_PAGES(GuiStack ?
257 KERNEL_LARGE_STACK_SIZE : KERNEL_STACK_SIZE);
258
259 /* Acquire the PFN lock */
260 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
261
262 //
263 // Loop them
264 //
265 for (i = 0; i < StackPages; i++)
266 {
267 //
268 // Check if this is a valid PTE
269 //
270 if (PointerPte->u.Hard.Valid == 1)
271 {
272 /* Get the PTE's page */
273 PageFrameNumber = PFN_FROM_PTE(PointerPte);
274 Pfn1 = MiGetPfnEntry(PageFrameNumber);
275
276 /* Now get the page of the page table mapping it */
277 PageTableFrameNumber = Pfn1->u4.PteFrame;
278 Pfn2 = MiGetPfnEntry(PageTableFrameNumber);
279
280 /* Remove a shared reference, since the page is going away */
281 MiDecrementShareCount(Pfn2, PageTableFrameNumber);
282
283 /* Set the special pending delete marker */
284 MI_SET_PFN_DELETED(Pfn1);
285
286 /* And now delete the actual stack page */
287 MiDecrementShareCount(Pfn1, PageFrameNumber);
288 }
289
290 //
291 // Next one
292 //
293 PointerPte--;
294 }
295
296 //
297 // We should be at the guard page now
298 //
299 ASSERT(PointerPte->u.Hard.Valid == 0);
300
301 /* Release the PFN lock */
302 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
303
304 //
305 // Release the PTEs
306 //
307 MiReleaseSystemPtes(PointerPte, StackPages + 1, SystemPteSpace);
308 }
309
310 PVOID
311 NTAPI
312 MmCreateKernelStack(IN BOOLEAN GuiStack,
313 IN UCHAR Node)
314 {
315 PFN_COUNT StackPtes, StackPages;
316 PMMPTE PointerPte, StackPte;
317 PVOID BaseAddress;
318 MMPTE TempPte, InvalidPte;
319 KIRQL OldIrql;
320 PFN_NUMBER PageFrameIndex;
321 ULONG i;
322 PMMPFN Pfn1;
323
324 //
325 // Calculate pages needed
326 //
327 if (GuiStack)
328 {
329 //
330 // We'll allocate 64KB stack, but only commit 12K
331 //
332 StackPtes = BYTES_TO_PAGES(KERNEL_LARGE_STACK_SIZE);
333 StackPages = BYTES_TO_PAGES(KERNEL_LARGE_STACK_COMMIT);
334
335 }
336 else
337 {
338 //
339 // If the dead stack S-LIST has a stack on it, use it instead of allocating
340 // new system PTEs for this stack
341 //
342 if (ExQueryDepthSList(&MmDeadStackSListHead))
343 {
344 Pfn1 = (PMMPFN)InterlockedPopEntrySList(&MmDeadStackSListHead);
345 if (Pfn1)
346 {
347 PointerPte = Pfn1->PteAddress;
348 BaseAddress = MiPteToAddress(++PointerPte);
349 return BaseAddress;
350 }
351 }
352
353 //
354 // We'll allocate 12K and that's it
355 //
356 StackPtes = BYTES_TO_PAGES(KERNEL_STACK_SIZE);
357 StackPages = StackPtes;
358 }
359
360 //
361 // Reserve stack pages, plus a guard page
362 //
363 StackPte = MiReserveSystemPtes(StackPtes + 1, SystemPteSpace);
364 if (!StackPte) return NULL;
365
366 //
367 // Get the stack address
368 //
369 BaseAddress = MiPteToAddress(StackPte + StackPtes + 1);
370
371 //
372 // Select the right PTE address where we actually start committing pages
373 //
374 PointerPte = StackPte;
375 if (GuiStack) PointerPte += BYTES_TO_PAGES(KERNEL_LARGE_STACK_SIZE -
376 KERNEL_LARGE_STACK_COMMIT);
377
378
379 /* Setup the temporary invalid PTE */
380 MI_MAKE_SOFTWARE_PTE(&InvalidPte, MM_NOACCESS);
381
382 /* Setup the template stack PTE */
383 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, PointerPte + 1, MM_READWRITE, 0);
384
385 //
386 // Acquire the PFN DB lock
387 //
388 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
389
390 //
391 // Loop each stack page
392 //
393 for (i = 0; i < StackPages; i++)
394 {
395 //
396 // Next PTE
397 //
398 PointerPte++;
399
400 /* Get a page and write the current invalid PTE */
401 MI_SET_USAGE(MI_USAGE_KERNEL_STACK);
402 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
403 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
404 MI_WRITE_INVALID_PTE(PointerPte, InvalidPte);
405
406 /* Initialize the PFN entry for this page */
407 MiInitializePfn(PageFrameIndex, PointerPte, 1);
408
409 /* Write the valid PTE */
410 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
411 MI_WRITE_VALID_PTE(PointerPte, TempPte);
412 }
413
414 //
415 // Release the PFN lock
416 //
417 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
418
419 //
420 // Return the stack address
421 //
422 return BaseAddress;
423 }
424
425 NTSTATUS
426 NTAPI
427 MmGrowKernelStackEx(IN PVOID StackPointer,
428 IN ULONG GrowSize)
429 {
430 PKTHREAD Thread = KeGetCurrentThread();
431 PMMPTE LimitPte, NewLimitPte, LastPte;
432 KIRQL OldIrql;
433 MMPTE TempPte, InvalidPte;
434 PFN_NUMBER PageFrameIndex;
435
436 //
437 // Make sure the stack did not overflow
438 //
439 ASSERT(((ULONG_PTR)Thread->StackBase - (ULONG_PTR)Thread->StackLimit) <=
440 (KERNEL_LARGE_STACK_SIZE + PAGE_SIZE));
441
442 //
443 // Get the current stack limit
444 //
445 LimitPte = MiAddressToPte(Thread->StackLimit);
446 ASSERT(LimitPte->u.Hard.Valid == 1);
447
448 //
449 // Get the new one and make sure this isn't a retarded request
450 //
451 NewLimitPte = MiAddressToPte((PVOID)((ULONG_PTR)StackPointer - GrowSize));
452 if (NewLimitPte == LimitPte) return STATUS_SUCCESS;
453
454 //
455 // Now make sure you're not going past the reserved space
456 //
457 LastPte = MiAddressToPte((PVOID)((ULONG_PTR)Thread->StackBase -
458 KERNEL_LARGE_STACK_SIZE));
459 if (NewLimitPte < LastPte)
460 {
461 //
462 // Sorry!
463 //
464 DPRINT1("Thread wants too much stack\n");
465 return STATUS_STACK_OVERFLOW;
466 }
467
468 //
469 // Calculate the number of new pages
470 //
471 LimitPte--;
472
473 /* Setup the temporary invalid PTE */
474 MI_MAKE_SOFTWARE_PTE(&InvalidPte, MM_NOACCESS);
475
476 //
477 // Acquire the PFN DB lock
478 //
479 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
480
481 //
482 // Loop each stack page
483 //
484 while (LimitPte >= NewLimitPte)
485 {
486 /* Get a page and write the current invalid PTE */
487 MI_SET_USAGE(MI_USAGE_KERNEL_STACK_EXPANSION);
488 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
489 PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
490 MI_WRITE_INVALID_PTE(LimitPte, InvalidPte);
491
492 /* Initialize the PFN entry for this page */
493 MiInitializePfn(PageFrameIndex, LimitPte, 1);
494
495 /* Setup the template stack PTE */
496 MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, LimitPte, MM_READWRITE, PageFrameIndex);
497
498 /* Write the valid PTE */
499 MI_WRITE_VALID_PTE(LimitPte--, TempPte);
500 }
501
502 //
503 // Release the PFN lock
504 //
505 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
506
507 //
508 // Set the new limit
509 //
510 Thread->StackLimit = (ULONG_PTR)MiPteToAddress(NewLimitPte);
511 return STATUS_SUCCESS;
512 }
513
514 NTSTATUS
515 NTAPI
516 MmGrowKernelStack(IN PVOID StackPointer)
517 {
518 //
519 // Call the extended version
520 //
521 return MmGrowKernelStackEx(StackPointer, KERNEL_LARGE_STACK_COMMIT);
522 }
523
524 NTSTATUS
525 NTAPI
526 MmSetMemoryPriorityProcess(IN PEPROCESS Process,
527 IN UCHAR MemoryPriority)
528 {
529 UCHAR OldPriority;
530
531 //
532 // Check if we have less then 16MB of Physical Memory
533 //
534 if ((MmSystemSize == MmSmallSystem) &&
535 (MmNumberOfPhysicalPages < ((15 * 1024 * 1024) / PAGE_SIZE)))
536 {
537 //
538 // Always use background priority
539 //
540 MemoryPriority = MEMORY_PRIORITY_BACKGROUND;
541 }
542
543 //
544 // Save the old priority and update it
545 //
546 OldPriority = (UCHAR)Process->Vm.Flags.MemoryPriority;
547 Process->Vm.Flags.MemoryPriority = MemoryPriority;
548
549 //
550 // Return the old priority
551 //
552 return OldPriority;
553 }
554
555 LCID
556 NTAPI
557 MmGetSessionLocaleId(VOID)
558 {
559 PEPROCESS Process;
560 PAGED_CODE();
561
562 //
563 // Get the current process
564 //
565 Process = PsGetCurrentProcess();
566
567 //
568 // Check if it's the Session Leader
569 //
570 if (Process->Vm.Flags.SessionLeader)
571 {
572 //
573 // Make sure it has a valid Session
574 //
575 if (Process->Session)
576 {
577 //
578 // Get the Locale ID
579 //
580 #if ROS_HAS_SESSIONS
581 return ((PMM_SESSION_SPACE)Process->Session)->LocaleId;
582 #endif
583 }
584 }
585
586 //
587 // Not a session leader, return the default
588 //
589 return PsDefaultThreadLocaleId;
590 }
591
592 NTSTATUS
593 NTAPI
594 MmCreatePeb(IN PEPROCESS Process,
595 IN PINITIAL_PEB InitialPeb,
596 OUT PPEB *BasePeb)
597 {
598 PPEB Peb = NULL;
599 LARGE_INTEGER SectionOffset;
600 SIZE_T ViewSize = 0;
601 PVOID TableBase = NULL;
602 PIMAGE_NT_HEADERS NtHeaders;
603 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
604 NTSTATUS Status;
605 USHORT Characteristics;
606 KAFFINITY ProcessAffinityMask = 0;
607 SectionOffset.QuadPart = (ULONGLONG)0;
608 *BasePeb = NULL;
609
610 //
611 // Attach to Process
612 //
613 KeAttachProcess(&Process->Pcb);
614
615 //
616 // Map NLS Tables
617 //
618 Status = MmMapViewOfSection(ExpNlsSectionPointer,
619 (PEPROCESS)Process,
620 &TableBase,
621 0,
622 0,
623 &SectionOffset,
624 &ViewSize,
625 ViewShare,
626 MEM_TOP_DOWN,
627 PAGE_READONLY);
628 DPRINT("NLS Tables at: %p\n", TableBase);
629 if (!NT_SUCCESS(Status))
630 {
631 /* Cleanup and exit */
632 KeDetachProcess();
633 return Status;
634 }
635
636 //
637 // Allocate the PEB
638 //
639 Status = MiCreatePebOrTeb(Process, sizeof(PEB), (PULONG_PTR)&Peb);
640 DPRINT("PEB at: %p\n", Peb);
641 if (!NT_SUCCESS(Status))
642 {
643 /* Cleanup and exit */
644 KeDetachProcess();
645 return Status;
646 }
647
648 //
649 // Use SEH in case we can't load the PEB
650 //
651 _SEH2_TRY
652 {
653 //
654 // Initialize the PEB
655 //
656 RtlZeroMemory(Peb, sizeof(PEB));
657
658 //
659 // Set up data
660 //
661 Peb->ImageBaseAddress = Process->SectionBaseAddress;
662 Peb->InheritedAddressSpace = InitialPeb->InheritedAddressSpace;
663 Peb->Mutant = InitialPeb->Mutant;
664 Peb->ImageUsesLargePages = InitialPeb->ImageUsesLargePages;
665
666 //
667 // NLS
668 //
669 Peb->AnsiCodePageData = (PCHAR)TableBase + ExpAnsiCodePageDataOffset;
670 Peb->OemCodePageData = (PCHAR)TableBase + ExpOemCodePageDataOffset;
671 Peb->UnicodeCaseTableData = (PCHAR)TableBase + ExpUnicodeCaseTableDataOffset;
672
673 //
674 // Default Version Data (could get changed below)
675 //
676 Peb->OSMajorVersion = NtMajorVersion;
677 Peb->OSMinorVersion = NtMinorVersion;
678 Peb->OSBuildNumber = (USHORT)(NtBuildNumber & 0x3FFF);
679 Peb->OSPlatformId = 2; /* VER_PLATFORM_WIN32_NT */
680 Peb->OSCSDVersion = (USHORT)CmNtCSDVersion;
681
682 //
683 // Heap and Debug Data
684 //
685 Peb->NumberOfProcessors = KeNumberProcessors;
686 Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL);
687 Peb->NtGlobalFlag = NtGlobalFlag;
688 /*Peb->HeapSegmentReserve = MmHeapSegmentReserve;
689 Peb->HeapSegmentCommit = MmHeapSegmentCommit;
690 Peb->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
691 Peb->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
692 Peb->CriticalSectionTimeout = MmCriticalSectionTimeout;
693 Peb->MinimumStackCommit = MmMinimumStackCommitInBytes;
694 */
695 Peb->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB)) / sizeof(PVOID);
696 Peb->ProcessHeaps = (PVOID*)(Peb + 1);
697
698 //
699 // Session ID
700 //
701 if (Process->Session) Peb->SessionId = 0; // MmGetSessionId(Process);
702 }
703 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
704 {
705 //
706 // Fail
707 //
708 KeDetachProcess();
709 _SEH2_YIELD(return _SEH2_GetExceptionCode());
710 }
711 _SEH2_END;
712
713 //
714 // Use SEH in case we can't load the image
715 //
716 _SEH2_TRY
717 {
718 //
719 // Get NT Headers
720 //
721 NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress);
722 Characteristics = NtHeaders->FileHeader.Characteristics;
723 }
724 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
725 {
726 //
727 // Fail
728 //
729 KeDetachProcess();
730 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT);
731 }
732 _SEH2_END;
733
734 //
735 // Parse the headers
736 //
737 if (NtHeaders)
738 {
739 //
740 // Use SEH in case we can't load the headers
741 //
742 _SEH2_TRY
743 {
744 //
745 // Get the Image Config Data too
746 //
747 ImageConfigData = RtlImageDirectoryEntryToData(Peb->ImageBaseAddress,
748 TRUE,
749 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
750 (PULONG)&ViewSize);
751 if (ImageConfigData)
752 {
753 //
754 // Probe it
755 //
756 ProbeForRead(ImageConfigData,
757 sizeof(IMAGE_LOAD_CONFIG_DIRECTORY),
758 sizeof(ULONG));
759 }
760
761 //
762 // Write subsystem data
763 //
764 Peb->ImageSubsystem = NtHeaders->OptionalHeader.Subsystem;
765 Peb->ImageSubsystemMajorVersion = NtHeaders->OptionalHeader.MajorSubsystemVersion;
766 Peb->ImageSubsystemMinorVersion = NtHeaders->OptionalHeader.MinorSubsystemVersion;
767
768 //
769 // Check for version data
770 //
771 if (NtHeaders->OptionalHeader.Win32VersionValue)
772 {
773 //
774 // Extract values and write them
775 //
776 Peb->OSMajorVersion = NtHeaders->OptionalHeader.Win32VersionValue & 0xFF;
777 Peb->OSMinorVersion = (NtHeaders->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
778 Peb->OSBuildNumber = (NtHeaders->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF;
779 Peb->OSPlatformId = (NtHeaders->OptionalHeader.Win32VersionValue >> 30) ^ 2;
780
781 /* Process CSD version override */
782 if ((ImageConfigData) && (ImageConfigData->CSDVersion))
783 {
784 /* Take the value from the image configuration directory */
785 Peb->OSCSDVersion = ImageConfigData->CSDVersion;
786 }
787 }
788
789 /* Process optional process affinity mask override */
790 if ((ImageConfigData) && (ImageConfigData->ProcessAffinityMask))
791 {
792 /* Take the value from the image configuration directory */
793 ProcessAffinityMask = ImageConfigData->ProcessAffinityMask;
794 }
795
796 //
797 // Check if this is a UP image
798 if (Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY)
799 {
800 //
801 // Force it to use CPU 0
802 //
803 /* FIXME: this should use the MmRotatingUniprocessorNumber */
804 Peb->ImageProcessAffinityMask = 0;
805 }
806 else
807 {
808 //
809 // Whatever was configured
810 //
811 Peb->ImageProcessAffinityMask = ProcessAffinityMask;
812 }
813 }
814 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
815 {
816 //
817 // Fail
818 //
819 KeDetachProcess();
820 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT);
821 }
822 _SEH2_END;
823 }
824
825 //
826 // Detach from the Process
827 //
828 KeDetachProcess();
829 *BasePeb = Peb;
830 return STATUS_SUCCESS;
831 }
832
833 NTSTATUS
834 NTAPI
835 MmCreateTeb(IN PEPROCESS Process,
836 IN PCLIENT_ID ClientId,
837 IN PINITIAL_TEB InitialTeb,
838 OUT PTEB *BaseTeb)
839 {
840 PTEB Teb;
841 NTSTATUS Status = STATUS_SUCCESS;
842 *BaseTeb = NULL;
843
844 //
845 // Attach to Target
846 //
847 KeAttachProcess(&Process->Pcb);
848
849 //
850 // Allocate the TEB
851 //
852 Status = MiCreatePebOrTeb(Process, sizeof(TEB), (PULONG_PTR)&Teb);
853 ASSERT(NT_SUCCESS(Status));
854
855 //
856 // Use SEH in case we can't load the TEB
857 //
858 _SEH2_TRY
859 {
860 //
861 // Initialize the PEB
862 //
863 RtlZeroMemory(Teb, sizeof(TEB));
864
865 //
866 // Set TIB Data
867 //
868 #ifdef _M_AMD64
869 Teb->NtTib.ExceptionList = NULL;
870 #else
871 Teb->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
872 #endif
873 Teb->NtTib.Self = (PNT_TIB)Teb;
874
875 //
876 // Identify this as an OS/2 V3.0 ("Cruiser") TIB
877 //
878 Teb->NtTib.Version = 30 << 8;
879
880 //
881 // Set TEB Data
882 //
883 Teb->ClientId = *ClientId;
884 Teb->RealClientId = *ClientId;
885 Teb->ProcessEnvironmentBlock = Process->Peb;
886 Teb->CurrentLocale = PsDefaultThreadLocaleId;
887
888 //
889 // Check if we have a grandparent TEB
890 //
891 if ((InitialTeb->PreviousStackBase == NULL) &&
892 (InitialTeb->PreviousStackLimit == NULL))
893 {
894 //
895 // Use initial TEB values
896 //
897 Teb->NtTib.StackBase = InitialTeb->StackBase;
898 Teb->NtTib.StackLimit = InitialTeb->StackLimit;
899 Teb->DeallocationStack = InitialTeb->AllocatedStackBase;
900 }
901 else
902 {
903 //
904 // Use grandparent TEB values
905 //
906 Teb->NtTib.StackBase = InitialTeb->PreviousStackBase;
907 Teb->NtTib.StackLimit = InitialTeb->PreviousStackLimit;
908 }
909
910 //
911 // Initialize the static unicode string
912 //
913 Teb->StaticUnicodeString.MaximumLength = sizeof(Teb->StaticUnicodeBuffer);
914 Teb->StaticUnicodeString.Buffer = Teb->StaticUnicodeBuffer;
915 }
916 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
917 {
918 //
919 // Get error code
920 //
921 Status = _SEH2_GetExceptionCode();
922 }
923 _SEH2_END;
924
925 //
926 // Return
927 //
928 KeDetachProcess();
929 *BaseTeb = Teb;
930 return Status;
931 }
932
933 VOID
934 NTAPI
935 MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess)
936 {
937 PMMPFN Pfn1;
938 PMMPTE sysPte;
939 MMPTE tempPte;
940
941 /* Setup some bogus list data */
942 MmWorkingSetList->LastEntry = CurrentProcess->Vm.MinimumWorkingSetSize;
943 MmWorkingSetList->HashTable = NULL;
944 MmWorkingSetList->HashTableSize = 0;
945 MmWorkingSetList->NumberOfImageWaiters = 0;
946 MmWorkingSetList->Wsle = (PVOID)0xDEADBABE;
947 MmWorkingSetList->VadBitMapHint = 1;
948 MmWorkingSetList->HashTableStart = (PVOID)0xBADAB00B;
949 MmWorkingSetList->HighestPermittedHashAddress = (PVOID)0xCAFEBABE;
950 MmWorkingSetList->FirstFree = 1;
951 MmWorkingSetList->FirstDynamic = 2;
952 MmWorkingSetList->NextSlot = 3;
953 MmWorkingSetList->LastInitializedWsle = 4;
954
955 /* The rule is that the owner process is always in the FLINK of the PDE's PFN entry */
956 Pfn1 = MiGetPfnEntry(CurrentProcess->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT);
957 ASSERT(Pfn1->u4.PteFrame == MiGetPfnEntryIndex(Pfn1));
958 Pfn1->u1.Event = (PKEVENT)CurrentProcess;
959
960 /* Map the process working set in kernel space */
961 sysPte = MiReserveSystemPtes(1, SystemPteSpace);
962 MI_MAKE_HARDWARE_PTE_KERNEL(&tempPte, sysPte, MM_READWRITE, CurrentProcess->WorkingSetPage);
963 MI_WRITE_VALID_PTE(sysPte, tempPte);
964 CurrentProcess->Vm.VmWorkingSetList = MiPteToAddress(sysPte);
965 }
966
967 NTSTATUS
968 NTAPI
969 MmInitializeProcessAddressSpace(IN PEPROCESS Process,
970 IN PEPROCESS ProcessClone OPTIONAL,
971 IN PVOID Section OPTIONAL,
972 IN OUT PULONG Flags,
973 IN POBJECT_NAME_INFORMATION *AuditName OPTIONAL)
974 {
975 NTSTATUS Status = STATUS_SUCCESS;
976 SIZE_T ViewSize = 0;
977 PVOID ImageBase = 0;
978 PROS_SECTION_OBJECT SectionObject = Section;
979 PMMPTE PointerPte;
980 KIRQL OldIrql;
981 PMMPDE PointerPde;
982 PFN_NUMBER PageFrameNumber;
983 UNICODE_STRING FileName;
984 PWCHAR Source;
985 PCHAR Destination;
986 USHORT Length = 0;
987 MMPTE TempPte;
988
989 /* We should have a PDE */
990 ASSERT(Process->Pcb.DirectoryTableBase[0] != 0);
991 ASSERT(Process->PdeUpdateNeeded == FALSE);
992
993 /* Attach to the process */
994 KeAttachProcess(&Process->Pcb);
995
996 /* The address space should now been in phase 1 or 0 */
997 ASSERT(Process->AddressSpaceInitialized <= 1);
998 Process->AddressSpaceInitialized = 2;
999
1000 /* Initialize the Addresss Space lock */
1001 KeInitializeGuardedMutex(&Process->AddressCreationLock);
1002 Process->Vm.WorkingSetExpansionLinks.Flink = NULL;
1003
1004 /* Initialize AVL tree */
1005 ASSERT(Process->VadRoot.NumberGenericTableElements == 0);
1006 Process->VadRoot.BalancedRoot.u1.Parent = &Process->VadRoot.BalancedRoot;
1007
1008 /* Lock PFN database */
1009 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1010
1011 /* Setup the PFN for the PDE base of this process */
1012 #ifdef _M_AMD64
1013 PointerPte = MiAddressToPte(PXE_BASE);
1014 #else
1015 PointerPte = MiAddressToPte(PDE_BASE);
1016 #endif
1017 PageFrameNumber = PFN_FROM_PTE(PointerPte);
1018 ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE);
1019 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
1020
1021 /* Do the same for hyperspace */
1022 #ifdef _M_AMD64
1023 PointerPde = MiAddressToPxe((PVOID)HYPER_SPACE);
1024 #else
1025 PointerPde = MiAddressToPde(HYPER_SPACE);
1026 #endif
1027 PageFrameNumber = PFN_FROM_PTE(PointerPde);
1028 //ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE); // we're not lucky
1029 MiInitializePfn(PageFrameNumber, (PMMPTE)PointerPde, TRUE);
1030
1031 /* Setup the PFN for the PTE for the working set */
1032 PointerPte = MiAddressToPte(MI_WORKING_SET_LIST);
1033 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, MM_READWRITE, 0);
1034 ASSERT(PointerPte->u.Long != 0);
1035 PageFrameNumber = PFN_FROM_PTE(PointerPte);
1036 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPte);
1037 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
1038 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
1039 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1040
1041 /* Now initialize the working set list */
1042 MiInitializeWorkingSetList(Process);
1043
1044 /* Sanity check */
1045 ASSERT(Process->PhysicalVadRoot == NULL);
1046
1047 /* Release PFN lock */
1048 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1049
1050 /* Lock the VAD, ARM3-owned ranges away */
1051 MiRosTakeOverSharedUserPage(Process);
1052
1053 /* Check if there's a Section Object */
1054 if (SectionObject)
1055 {
1056 /* Determine the image file name and save it to EPROCESS */
1057 FileName = SectionObject->FileObject->FileName;
1058 Source = (PWCHAR)((PCHAR)FileName.Buffer + FileName.Length);
1059 if (FileName.Buffer)
1060 {
1061 /* Loop the file name*/
1062 while (Source > FileName.Buffer)
1063 {
1064 /* Make sure this isn't a backslash */
1065 if (*--Source == OBJ_NAME_PATH_SEPARATOR)
1066 {
1067 /* If so, stop it here */
1068 Source++;
1069 break;
1070 }
1071 else
1072 {
1073 /* Otherwise, keep going */
1074 Length++;
1075 }
1076 }
1077 }
1078
1079 /* Copy the to the process and truncate it to 15 characters if necessary */
1080 Destination = Process->ImageFileName;
1081 Length = min(Length, sizeof(Process->ImageFileName) - 1);
1082 while (Length--) *Destination++ = (UCHAR)*Source++;
1083 *Destination = ANSI_NULL;
1084
1085 /* Check if caller wants an audit name */
1086 if (AuditName)
1087 {
1088 /* Setup the audit name */
1089 Status = SeInitializeProcessAuditName(SectionObject->FileObject,
1090 FALSE,
1091 AuditName);
1092 if (!NT_SUCCESS(Status))
1093 {
1094 /* Fail */
1095 KeDetachProcess();
1096 return Status;
1097 }
1098 }
1099
1100 /* Map the section */
1101 Status = MmMapViewOfSection(Section,
1102 Process,
1103 (PVOID*)&ImageBase,
1104 0,
1105 0,
1106 NULL,
1107 &ViewSize,
1108 0,
1109 MEM_COMMIT,
1110 PAGE_READWRITE);
1111
1112 /* Save the pointer */
1113 Process->SectionBaseAddress = ImageBase;
1114 }
1115
1116 /* Be nice and detach */
1117 KeDetachProcess();
1118
1119 /* Return status to caller */
1120 return Status;
1121 }
1122
1123 NTSTATUS
1124 NTAPI
1125 INIT_FUNCTION
1126 MmInitializeHandBuiltProcess(IN PEPROCESS Process,
1127 IN PULONG_PTR DirectoryTableBase)
1128 {
1129 /* Share the directory base with the idle process */
1130 DirectoryTableBase[0] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[0];
1131 DirectoryTableBase[1] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[1];
1132
1133 /* Initialize the Addresss Space */
1134 KeInitializeGuardedMutex(&Process->AddressCreationLock);
1135 KeInitializeSpinLock(&Process->HyperSpaceLock);
1136 Process->Vm.WorkingSetExpansionLinks.Flink = NULL;
1137 ASSERT(Process->VadRoot.NumberGenericTableElements == 0);
1138 Process->VadRoot.BalancedRoot.u1.Parent = &Process->VadRoot.BalancedRoot;
1139
1140 /* Use idle process Working set */
1141 Process->Vm.VmWorkingSetList = PsGetCurrentProcess()->Vm.VmWorkingSetList;
1142
1143 /* Done */
1144 Process->HasAddressSpace = TRUE;//??
1145 return STATUS_SUCCESS;
1146 }
1147
1148 NTSTATUS
1149 NTAPI
1150 INIT_FUNCTION
1151 MmInitializeHandBuiltProcess2(IN PEPROCESS Process)
1152 {
1153 /* Lock the VAD, ARM3-owned ranges away */
1154 MiRosTakeOverSharedUserPage(Process);
1155 return STATUS_SUCCESS;
1156 }
1157
1158 #ifdef _M_IX86
1159 /* FIXME: Evaluate ways to make this portable yet arch-specific */
1160 BOOLEAN
1161 NTAPI
1162 MmCreateProcessAddressSpace(IN ULONG MinWs,
1163 IN PEPROCESS Process,
1164 OUT PULONG_PTR DirectoryTableBase)
1165 {
1166 KIRQL OldIrql;
1167 PFN_NUMBER PdeIndex, HyperIndex, WsListIndex;
1168 PMMPTE PointerPte;
1169 MMPTE TempPte, PdePte;
1170 ULONG PdeOffset;
1171 PMMPTE SystemTable, HyperTable;
1172 ULONG Color;
1173 PMMPFN Pfn1;
1174
1175 /* Choose a process color */
1176 Process->NextPageColor = (USHORT)RtlRandom(&MmProcessColorSeed);
1177
1178 /* Setup the hyperspace lock */
1179 KeInitializeSpinLock(&Process->HyperSpaceLock);
1180
1181 /* Lock PFN database */
1182 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1183
1184 /* Get a zero page for the PDE, if possible */
1185 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1186 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY);
1187 PdeIndex = MiRemoveZeroPageSafe(Color);
1188 if (!PdeIndex)
1189 {
1190 /* No zero pages, grab a free one */
1191 PdeIndex = MiRemoveAnyPage(Color);
1192
1193 /* Zero it outside the PFN lock */
1194 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1195 MiZeroPhysicalPage(PdeIndex);
1196 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1197 }
1198
1199 /* Get a zero page for hyperspace, if possible */
1200 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY);
1201 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1202 HyperIndex = MiRemoveZeroPageSafe(Color);
1203 if (!HyperIndex)
1204 {
1205 /* No zero pages, grab a free one */
1206 HyperIndex = MiRemoveAnyPage(Color);
1207
1208 /* Zero it outside the PFN lock */
1209 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1210 MiZeroPhysicalPage(HyperIndex);
1211 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1212 }
1213
1214 /* Get a zero page for the woring set list, if possible */
1215 MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
1216 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1217 WsListIndex = MiRemoveZeroPageSafe(Color);
1218 if (!WsListIndex)
1219 {
1220 /* No zero pages, grab a free one */
1221 WsListIndex = MiRemoveAnyPage(Color);
1222
1223 /* Zero it outside the PFN lock */
1224 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1225 MiZeroPhysicalPage(WsListIndex);
1226 }
1227 else
1228 {
1229 /* Release the PFN lock */
1230 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1231 }
1232
1233 /* Switch to phase 1 initialization */
1234 ASSERT(Process->AddressSpaceInitialized == 0);
1235 Process->AddressSpaceInitialized = 1;
1236
1237 /* Set the base directory pointers */
1238 Process->WorkingSetPage = WsListIndex;
1239 DirectoryTableBase[0] = PdeIndex << PAGE_SHIFT;
1240 DirectoryTableBase[1] = HyperIndex << PAGE_SHIFT;
1241
1242 /* Make sure we don't already have a page directory setup */
1243 ASSERT(Process->Pcb.DirectoryTableBase[0] == 0);
1244
1245 /* Get a PTE to map hyperspace */
1246 PointerPte = MiReserveSystemPtes(1, SystemPteSpace);
1247 ASSERT(PointerPte != NULL);
1248
1249 /* Build it */
1250 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte,
1251 PointerPte,
1252 MM_READWRITE,
1253 HyperIndex);
1254
1255 /* Set it dirty and map it */
1256 MI_MAKE_DIRTY_PAGE(&PdePte);
1257 MI_WRITE_VALID_PTE(PointerPte, PdePte);
1258
1259 /* Now get hyperspace's page table */
1260 HyperTable = MiPteToAddress(PointerPte);
1261
1262 /* Now write the PTE/PDE entry for the working set list index itself */
1263 TempPte = ValidKernelPte;
1264 TempPte.u.Hard.PageFrameNumber = WsListIndex;
1265 /* Hyperspace is local */
1266 MI_MAKE_LOCAL_PAGE(&TempPte);
1267 PdeOffset = MiAddressToPteOffset(MmWorkingSetList);
1268 HyperTable[PdeOffset] = TempPte;
1269
1270 /* Let go of the system PTE */
1271 MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace);
1272
1273 /* Save the PTE address of the page directory itself */
1274 Pfn1 = MiGetPfnEntry(PdeIndex);
1275 Pfn1->PteAddress = (PMMPTE)PDE_BASE;
1276
1277 /* Insert us into the Mm process list */
1278 InsertTailList(&MmProcessList, &Process->MmProcessLinks);
1279
1280 /* Get a PTE to map the page directory */
1281 PointerPte = MiReserveSystemPtes(1, SystemPteSpace);
1282 ASSERT(PointerPte != NULL);
1283
1284 /* Build it */
1285 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte,
1286 PointerPte,
1287 MM_READWRITE,
1288 PdeIndex);
1289
1290 /* Set it dirty and map it */
1291 MI_MAKE_DIRTY_PAGE(&PdePte);
1292 MI_WRITE_VALID_PTE(PointerPte, PdePte);
1293
1294 /* Now get the page directory (which we'll double map, so call it a page table */
1295 SystemTable = MiPteToAddress(PointerPte);
1296
1297 /* Copy all the kernel mappings */
1298 PdeOffset = MiGetPdeOffset(MmSystemRangeStart);
1299 RtlCopyMemory(&SystemTable[PdeOffset],
1300 MiAddressToPde(MmSystemRangeStart),
1301 PAGE_SIZE - PdeOffset * sizeof(MMPTE));
1302
1303 /* Now write the PTE/PDE entry for hyperspace itself */
1304 TempPte = ValidKernelPte;
1305 TempPte.u.Hard.PageFrameNumber = HyperIndex;
1306 PdeOffset = MiGetPdeOffset(HYPER_SPACE);
1307 SystemTable[PdeOffset] = TempPte;
1308
1309 /* Sanity check */
1310 PdeOffset++;
1311 ASSERT(MiGetPdeOffset(MmHyperSpaceEnd) >= PdeOffset);
1312
1313 /* Now do the x86 trick of making the PDE a page table itself */
1314 PdeOffset = MiGetPdeOffset(PTE_BASE);
1315 TempPte.u.Hard.PageFrameNumber = PdeIndex;
1316 SystemTable[PdeOffset] = TempPte;
1317
1318 /* Let go of the system PTE */
1319 MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace);
1320 return TRUE;
1321 }
1322 #endif
1323
1324 VOID
1325 NTAPI
1326 MmCleanProcessAddressSpace(IN PEPROCESS Process)
1327 {
1328 PMMVAD Vad;
1329 PMM_AVL_TABLE VadTree;
1330 PETHREAD Thread = PsGetCurrentThread();
1331
1332 /* Only support this */
1333 ASSERT(Process->AddressSpaceInitialized == 2);
1334
1335 /* Lock the process address space from changes */
1336 MmLockAddressSpace(&Process->Vm);
1337
1338 /* VM is deleted now */
1339 Process->VmDeleted = TRUE;
1340
1341 /* Enumerate the VADs */
1342 VadTree = &Process->VadRoot;
1343 while (VadTree->NumberGenericTableElements)
1344 {
1345 /* Grab the current VAD */
1346 Vad = (PMMVAD)VadTree->BalancedRoot.RightChild;
1347
1348 /* Lock the working set */
1349 MiLockProcessWorkingSet(Process, Thread);
1350
1351 /* Remove this VAD from the tree */
1352 ASSERT(VadTree->NumberGenericTableElements >= 1);
1353 MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree);
1354
1355 /* Only regular VADs supported for now */
1356 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1357
1358 /* Check if this is a section VAD */
1359 if (!(Vad->u.VadFlags.PrivateMemory) && (Vad->ControlArea))
1360 {
1361 /* Remove the view */
1362 MiRemoveMappedView(Process, Vad);
1363 }
1364 else
1365 {
1366 /* Delete the addresses */
1367 MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
1368 (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
1369 Vad);
1370
1371 /* Release the working set */
1372 MiUnlockProcessWorkingSet(Process, Thread);
1373 }
1374
1375 /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */
1376 if (Vad->u.VadFlags.Spare == 1)
1377 {
1378 /* Set a flag so MmDeleteMemoryArea knows to free, but not to remove */
1379 Vad->u.VadFlags.Spare = 2;
1380 continue;
1381 }
1382
1383 /* Free the VAD memory */
1384 ExFreePool(Vad);
1385 }
1386 /* Delete the shared user data section */
1387 MiDeleteVirtualAddresses(USER_SHARED_DATA, USER_SHARED_DATA, NULL);
1388
1389 /* Release the address space */
1390 MmUnlockAddressSpace(&Process->Vm);
1391 }
1392
1393 VOID
1394 NTAPI
1395 MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
1396 {
1397 PMMPFN Pfn1, Pfn2;
1398 KIRQL OldIrql;
1399 PFN_NUMBER PageFrameIndex;
1400
1401 //ASSERT(Process->CommitCharge == 0);
1402
1403 /* Acquire the PFN lock */
1404 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1405
1406 /* Check for fully initialized process */
1407 if (Process->AddressSpaceInitialized == 2)
1408 {
1409 /* Map the working set page and its page table */
1410 Pfn1 = MiGetPfnEntry(Process->WorkingSetPage);
1411 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
1412
1413 /* Nuke it */
1414 MI_SET_PFN_DELETED(Pfn1);
1415 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
1416 MiDecrementShareCount(Pfn1, Process->WorkingSetPage);
1417 ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
1418 MiReleaseSystemPtes(MiAddressToPte(Process->Vm.VmWorkingSetList), 1, SystemPteSpace);
1419
1420 /* Now map hyperspace and its page table */
1421 PageFrameIndex = Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT;
1422 Pfn1 = MiGetPfnEntry(PageFrameIndex);
1423 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
1424
1425 /* Nuke it */
1426 MI_SET_PFN_DELETED(Pfn1);
1427 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
1428 MiDecrementShareCount(Pfn1, PageFrameIndex);
1429 ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
1430
1431 /* Finally, nuke the PDE itself */
1432 PageFrameIndex = Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT;
1433 Pfn1 = MiGetPfnEntry(PageFrameIndex);
1434 MI_SET_PFN_DELETED(Pfn1);
1435 MiDecrementShareCount(Pfn1, PageFrameIndex);
1436 MiDecrementShareCount(Pfn1, PageFrameIndex);
1437
1438 /* Page table is now dead. Bye bye... */
1439 ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
1440 }
1441 else
1442 {
1443 /* A partly-initialized process should never exit through here */
1444 ASSERT(FALSE);
1445 }
1446
1447 /* Release the PFN lock */
1448 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1449
1450 /* No support for sessions yet */
1451 ASSERT(Process->Session == 0);
1452
1453 /* Clear out the PDE pages */
1454 Process->Pcb.DirectoryTableBase[0] = 0;
1455 Process->Pcb.DirectoryTableBase[1] = 0;
1456 }
1457
1458 /* SESSION CODE TO MOVE TO SESSION.C ******************************************/
1459
1460 PMM_SESSION_SPACE MmSessionSpace;
1461 PFN_NUMBER MiSessionDataPages, MiSessionTagPages, MiSessionTagSizePages;
1462 PFN_NUMBER MiSessionBigPoolPages, MiSessionCreateCharge;
1463 KGUARDED_MUTEX MiSessionIdMutex;
1464 LONG MmSessionDataPages;
1465 PRTL_BITMAP MiSessionIdBitmap;
1466 volatile LONG MiSessionLeaderExists;
1467
1468 VOID
1469 NTAPI
1470 MiInitializeSessionIds(VOID)
1471 {
1472 ULONG Size, BitmapSize;
1473 PFN_NUMBER TotalPages;
1474
1475 /* Setup the total number of data pages needed for the structure */
1476 TotalPages = MI_SESSION_DATA_PAGES_MAXIMUM;
1477 MiSessionDataPages = ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE)) >> PAGE_SHIFT;
1478 ASSERT(MiSessionDataPages <= MI_SESSION_DATA_PAGES_MAXIMUM - 3);
1479 TotalPages -= MiSessionDataPages;
1480
1481 /* Setup the number of pages needed for session pool tags */
1482 MiSessionTagSizePages = 2;
1483 MiSessionBigPoolPages = 1;
1484 MiSessionTagPages = MiSessionTagSizePages + MiSessionBigPoolPages;
1485 ASSERT(MiSessionTagPages <= TotalPages);
1486 ASSERT(MiSessionTagPages < MI_SESSION_TAG_PAGES_MAXIMUM);
1487
1488 /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
1489 MiSessionCreateCharge = 1 + MiSessionDataPages + MiSessionTagPages;
1490
1491 /* Initialize the lock */
1492 KeInitializeGuardedMutex(&MiSessionIdMutex);
1493
1494 /* Allocate the bitmap */
1495 Size = MI_INITIAL_SESSION_IDS;
1496 BitmapSize = ((Size + 31) / 32) * sizeof(ULONG);
1497 MiSessionIdBitmap = ExAllocatePoolWithTag(PagedPool,
1498 sizeof(RTL_BITMAP) + BitmapSize,
1499 ' mM');
1500 if (MiSessionIdBitmap)
1501 {
1502 /* Free all the bits */
1503 RtlInitializeBitMap(MiSessionIdBitmap,
1504 (PVOID)(MiSessionIdBitmap + 1),
1505 Size);
1506 RtlClearAllBits(MiSessionIdBitmap);
1507 }
1508 else
1509 {
1510 /* Die if we couldn't allocate the bitmap */
1511 KeBugCheckEx(INSTALL_MORE_MEMORY,
1512 MmNumberOfPhysicalPages,
1513 MmLowestPhysicalPage,
1514 MmHighestPhysicalPage,
1515 0x200);
1516 }
1517 }
1518
1519 VOID
1520 NTAPI
1521 MiSessionLeader(IN PEPROCESS Process)
1522 {
1523 KIRQL OldIrql;
1524
1525 /* Set the flag while under the expansion lock */
1526 OldIrql = KeAcquireQueuedSpinLock(LockQueueExpansionLock);
1527 Process->Vm.Flags.SessionLeader = TRUE;
1528 KeReleaseQueuedSpinLock(LockQueueExpansionLock, OldIrql);
1529 }
1530
1531 NTSTATUS
1532 NTAPI
1533 MiSessionCreateInternal(OUT PULONG SessionId)
1534 {
1535 PEPROCESS Process = PsGetCurrentProcess();
1536 ULONG NewFlags, Flags, Size, i, Color;
1537 KIRQL OldIrql;
1538 PMMPTE PointerPte, PageTables, SessionPte;
1539 PMMPDE PointerPde;
1540 PMM_SESSION_SPACE SessionGlobal;
1541 MMPTE TempPte;
1542 NTSTATUS Status;
1543 BOOLEAN Result;
1544 PFN_NUMBER SessionPageDirIndex;
1545 PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM];
1546 PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM];
1547
1548 /* This should not exist yet */
1549 ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE);
1550
1551 /* Loop so we can set the session-is-creating flag */
1552 Flags = Process->Flags;
1553 while (TRUE)
1554 {
1555 /* Check if it's already set */
1556 if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT)
1557 {
1558 /* Bail out */
1559 DPRINT1("Lost session race\n");
1560 return STATUS_ALREADY_COMMITTED;
1561 }
1562
1563 /* Now try to set it */
1564 NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags,
1565 Flags | PSF_SESSION_CREATION_UNDERWAY_BIT,
1566 Flags);
1567 if (NewFlags == Flags) break;
1568
1569 /* It changed, try again */
1570 Flags = NewFlags;
1571 }
1572
1573 /* Now we should own the flag */
1574 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
1575
1576 /*
1577 * Session space covers everything from 0xA0000000 to 0xC0000000.
1578 * Allocate enough page tables to describe the entire region
1579 */
1580 Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE);
1581 PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM');
1582 ASSERT(PageTables != NULL);
1583 RtlZeroMemory(PageTables, Size);
1584
1585 /* Lock the session ID creation mutex */
1586 KeAcquireGuardedMutex(&MiSessionIdMutex);
1587
1588 /* Allocate a new Session ID */
1589 *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0);
1590 if (*SessionId == 0xFFFFFFFF)
1591 {
1592 /* We ran out of session IDs, we should expand */
1593 DPRINT1("Too many sessions created. Expansion not yet supported\n");
1594 return STATUS_NO_MEMORY;
1595 }
1596
1597 /* Unlock the session ID creation mutex */
1598 KeReleaseGuardedMutex(&MiSessionIdMutex);
1599
1600 /* Reserve the global PTEs */
1601 SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace);
1602 ASSERT(SessionPte != NULL);
1603
1604 /* Acquire the PFN lock while we set everything up */
1605 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1606
1607 /* Loop the global PTEs */
1608 TempPte.u.Long = ValidKernelPte.u.Long;
1609 for (i = 0; i < MiSessionDataPages; i++)
1610 {
1611 /* Get a zeroed colored zero page */
1612 Color = MI_GET_NEXT_COLOR();
1613 DataPage[i] = MiRemoveZeroPageSafe(Color);
1614 if (!DataPage[i])
1615 {
1616 /* No zero pages, grab a free one */
1617 DataPage[i] = MiRemoveAnyPage(Color);
1618
1619 /* Zero it outside the PFN lock */
1620 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1621 MiZeroPhysicalPage(DataPage[i]);
1622 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1623 }
1624
1625 /* Fill the PTE out */
1626 TempPte.u.Hard.PageFrameNumber = DataPage[i];
1627 MI_WRITE_VALID_PTE(SessionPte + i, TempPte);
1628 }
1629
1630 /* Set the pointer to global space */
1631 SessionGlobal = MiPteToAddress(SessionPte);
1632
1633 /* Get a zeroed colored zero page */
1634 Color = MI_GET_NEXT_COLOR();
1635 SessionPageDirIndex = MiRemoveZeroPageSafe(Color);
1636 if (!SessionPageDirIndex)
1637 {
1638 /* No zero pages, grab a free one */
1639 SessionPageDirIndex = MiRemoveAnyPage(Color);
1640
1641 /* Zero it outside the PFN lock */
1642 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1643 MiZeroPhysicalPage(SessionPageDirIndex);
1644 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1645 }
1646
1647 /* Fill the PTE out */
1648 TempPte.u.Long = ValidKernelPdeLocal.u.Long;
1649 TempPte.u.Hard.PageFrameNumber = SessionPageDirIndex;
1650
1651 /* Setup, allocate, fill out the MmSessionSpace PTE */
1652 PointerPde = MiAddressToPde(MmSessionSpace);
1653 ASSERT(PointerPde->u.Long == 0);
1654 MI_WRITE_VALID_PTE(PointerPde, TempPte);
1655 MiInitializePfnForOtherProcess(SessionPageDirIndex,
1656 PointerPde,
1657 SessionPageDirIndex);
1658 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0);
1659
1660 /* Loop all the local PTEs for it */
1661 TempPte.u.Long = ValidKernelPteLocal.u.Long;
1662 PointerPte = MiAddressToPte(MmSessionSpace);
1663 for (i = 0; i < MiSessionDataPages; i++)
1664 {
1665 /* And fill them out */
1666 TempPte.u.Hard.PageFrameNumber = DataPage[i];
1667 MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte);
1668 ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0);
1669 }
1670
1671 /* Finally loop all of the session pool tag pages */
1672 for (i = 0; i < MiSessionTagPages; i++)
1673 {
1674 /* Grab a zeroed colored page */
1675 Color = MI_GET_NEXT_COLOR();
1676 TagPage[i] = MiRemoveZeroPageSafe(Color);
1677 if (!TagPage[i])
1678 {
1679 /* No zero pages, grab a free one */
1680 TagPage[i] = MiRemoveAnyPage(Color);
1681
1682 /* Zero it outside the PFN lock */
1683 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1684 MiZeroPhysicalPage(TagPage[i]);
1685 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1686 }
1687
1688 /* Fill the PTE out */
1689 TempPte.u.Hard.PageFrameNumber = TagPage[i];
1690 MiInitializePfnAndMakePteValid(TagPage[i],
1691 PointerPte + MiSessionDataPages + i,
1692 TempPte);
1693 }
1694
1695 /* PTEs have been setup, release the PFN lock */
1696 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1697
1698 /* Fill out the session space structure now */
1699 MmSessionSpace->GlobalVirtualAddress = SessionGlobal;
1700 MmSessionSpace->ReferenceCount = 1;
1701 MmSessionSpace->ResidentProcessCount = 1;
1702 MmSessionSpace->u.LongFlags = 0;
1703 MmSessionSpace->SessionId = *SessionId;
1704 MmSessionSpace->LocaleId = PsDefaultSystemLocaleId;
1705 MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex;
1706 MmSessionSpace->Color = Color;
1707 MmSessionSpace->NonPageablePages = MiSessionCreateCharge;
1708 MmSessionSpace->CommittedPages = MiSessionCreateCharge;
1709 MmSessionSpace->PageTables = PageTables;
1710 MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde;
1711 InitializeListHead(&MmSessionSpace->ImageList);
1712 DPRINT1("Session %d is ready to go: 0x%p 0x%p, %lx 0x%p\n",
1713 *SessionId, MmSessionSpace, SessionGlobal, SessionPageDirIndex, PageTables);
1714
1715 /* Initialize session pool */
1716 //Status = MiInitializeSessionPool();
1717 Status = STATUS_SUCCESS;
1718 ASSERT(NT_SUCCESS(Status) == TRUE);
1719
1720 /* Initialize system space */
1721 Result = MiInitializeSystemSpaceMap(&SessionGlobal->Session);
1722 ASSERT(Result == TRUE);
1723
1724 /* Initialize the process list, make sure the workign set list is empty */
1725 ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
1726 ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
1727 InitializeListHead(&SessionGlobal->ProcessList);
1728
1729 /* We're done, clear the flag */
1730 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
1731 PspClearProcessFlag(Process, PSF_SESSION_CREATION_UNDERWAY_BIT);
1732
1733 /* Insert the process into the session */
1734 ASSERT(Process->Session == NULL);
1735 ASSERT(SessionGlobal->ProcessReferenceToSession == 0);
1736 SessionGlobal->ProcessReferenceToSession = 1;
1737
1738 /* We're done */
1739 InterlockedIncrement(&MmSessionDataPages);
1740 return STATUS_SUCCESS;
1741 }
1742
1743 NTSTATUS
1744 NTAPI
1745 MmSessionCreate(OUT PULONG SessionId)
1746 {
1747 PEPROCESS Process = PsGetCurrentProcess();
1748 ULONG SessionLeaderExists;
1749 NTSTATUS Status;
1750
1751 /* Fail if the process is already in a session */
1752 if (Process->Flags & PSF_PROCESS_IN_SESSION_BIT)
1753 {
1754 DPRINT1("Process already in session\n");
1755 return STATUS_ALREADY_COMMITTED;
1756 }
1757
1758 /* Check if the process is already the session leader */
1759 if (!Process->Vm.Flags.SessionLeader)
1760 {
1761 /* Atomically set it as the leader */
1762 SessionLeaderExists = InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0);
1763 if (SessionLeaderExists)
1764 {
1765 DPRINT1("Session leader race\n");
1766 return STATUS_INVALID_SYSTEM_SERVICE;
1767 }
1768
1769 /* Do the work required to upgrade him */
1770 MiSessionLeader(Process);
1771 }
1772
1773 /* FIXME: Actually create a session */
1774 KeEnterCriticalRegion();
1775 Status = MiSessionCreateInternal(SessionId);
1776 KeLeaveCriticalRegion();
1777
1778 /* Set and assert the flags, and return */
1779 PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT);
1780 ASSERT(MiSessionLeaderExists == 1);
1781 return Status;
1782 }
1783
1784 NTSTATUS
1785 NTAPI
1786 MmSessionDelete(IN ULONG SessionId)
1787 {
1788 PEPROCESS Process = PsGetCurrentProcess();
1789
1790 /* Process must be in a session */
1791 if (!(Process->Flags & PSF_PROCESS_IN_SESSION_BIT))
1792 {
1793 DPRINT1("Not in a session!\n");
1794 return STATUS_UNABLE_TO_FREE_VM;
1795 }
1796
1797 /* It must be the session leader */
1798 if (!Process->Vm.Flags.SessionLeader)
1799 {
1800 DPRINT1("Not a session leader!\n");
1801 return STATUS_UNABLE_TO_FREE_VM;
1802 }
1803
1804 /* Remove one reference count */
1805 KeEnterCriticalRegion();
1806 /* FIXME: Do it */
1807 KeLeaveCriticalRegion();
1808
1809 /* All done */
1810 return STATUS_SUCCESS;
1811 }
1812
1813 /* SYSTEM CALLS ***************************************************************/
1814
1815 NTSTATUS
1816 NTAPI
1817 NtAllocateUserPhysicalPages(IN HANDLE ProcessHandle,
1818 IN OUT PULONG_PTR NumberOfPages,
1819 IN OUT PULONG_PTR UserPfnArray)
1820 {
1821 UNIMPLEMENTED;
1822 return STATUS_NOT_IMPLEMENTED;
1823 }
1824
1825 NTSTATUS
1826 NTAPI
1827 NtMapUserPhysicalPages(IN PVOID VirtualAddresses,
1828 IN ULONG_PTR NumberOfPages,
1829 IN OUT PULONG_PTR UserPfnArray)
1830 {
1831 UNIMPLEMENTED;
1832 return STATUS_NOT_IMPLEMENTED;
1833 }
1834
1835 NTSTATUS
1836 NTAPI
1837 NtMapUserPhysicalPagesScatter(IN PVOID *VirtualAddresses,
1838 IN ULONG_PTR NumberOfPages,
1839 IN OUT PULONG_PTR UserPfnArray)
1840 {
1841 UNIMPLEMENTED;
1842 return STATUS_NOT_IMPLEMENTED;
1843 }
1844
1845 NTSTATUS
1846 NTAPI
1847 NtFreeUserPhysicalPages(IN HANDLE ProcessHandle,
1848 IN OUT PULONG_PTR NumberOfPages,
1849 IN OUT PULONG_PTR UserPfnArray)
1850 {
1851 UNIMPLEMENTED;
1852 return STATUS_NOT_IMPLEMENTED;
1853 }
1854
1855 /* EOF */