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