b1c50a2d92d989c570c4f1011e044d2087f49e7f
[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 MiLockProcessWorkingSetUnsafe(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 MiUnlockProcessWorkingSetUnsafe(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 MiLockProcessWorkingSetUnsafe(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 MiUnlockProcessWorkingSetUnsafe(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 return ((PMM_SESSION_SPACE)Process->Session)->LocaleId;
581 }
582 }
583
584 //
585 // Not a session leader, return the default
586 //
587 return PsDefaultThreadLocaleId;
588 }
589
590 NTSTATUS
591 NTAPI
592 MmCreatePeb(IN PEPROCESS Process,
593 IN PINITIAL_PEB InitialPeb,
594 OUT PPEB *BasePeb)
595 {
596 PPEB Peb = NULL;
597 LARGE_INTEGER SectionOffset;
598 SIZE_T ViewSize = 0;
599 PVOID TableBase = NULL;
600 PIMAGE_NT_HEADERS NtHeaders;
601 PIMAGE_LOAD_CONFIG_DIRECTORY ImageConfigData;
602 NTSTATUS Status;
603 USHORT Characteristics;
604 KAFFINITY ProcessAffinityMask = 0;
605 SectionOffset.QuadPart = (ULONGLONG)0;
606 *BasePeb = NULL;
607
608 //
609 // Attach to Process
610 //
611 KeAttachProcess(&Process->Pcb);
612
613 //
614 // Map NLS Tables
615 //
616 Status = MmMapViewOfSection(ExpNlsSectionPointer,
617 (PEPROCESS)Process,
618 &TableBase,
619 0,
620 0,
621 &SectionOffset,
622 &ViewSize,
623 ViewShare,
624 MEM_TOP_DOWN,
625 PAGE_READONLY);
626 DPRINT("NLS Tables at: %p\n", TableBase);
627 if (!NT_SUCCESS(Status))
628 {
629 /* Cleanup and exit */
630 KeDetachProcess();
631 return Status;
632 }
633
634 //
635 // Allocate the PEB
636 //
637 Status = MiCreatePebOrTeb(Process, sizeof(PEB), (PULONG_PTR)&Peb);
638 DPRINT("PEB at: %p\n", Peb);
639 if (!NT_SUCCESS(Status))
640 {
641 /* Cleanup and exit */
642 KeDetachProcess();
643 return Status;
644 }
645
646 //
647 // Use SEH in case we can't load the PEB
648 //
649 _SEH2_TRY
650 {
651 //
652 // Initialize the PEB
653 //
654 RtlZeroMemory(Peb, sizeof(PEB));
655
656 //
657 // Set up data
658 //
659 Peb->ImageBaseAddress = Process->SectionBaseAddress;
660 Peb->InheritedAddressSpace = InitialPeb->InheritedAddressSpace;
661 Peb->Mutant = InitialPeb->Mutant;
662 Peb->ImageUsesLargePages = InitialPeb->ImageUsesLargePages;
663
664 //
665 // NLS
666 //
667 Peb->AnsiCodePageData = (PCHAR)TableBase + ExpAnsiCodePageDataOffset;
668 Peb->OemCodePageData = (PCHAR)TableBase + ExpOemCodePageDataOffset;
669 Peb->UnicodeCaseTableData = (PCHAR)TableBase + ExpUnicodeCaseTableDataOffset;
670
671 //
672 // Default Version Data (could get changed below)
673 //
674 Peb->OSMajorVersion = NtMajorVersion;
675 Peb->OSMinorVersion = NtMinorVersion;
676 Peb->OSBuildNumber = (USHORT)(NtBuildNumber & 0x3FFF);
677 Peb->OSPlatformId = 2; /* VER_PLATFORM_WIN32_NT */
678 Peb->OSCSDVersion = (USHORT)CmNtCSDVersion;
679
680 //
681 // Heap and Debug Data
682 //
683 Peb->NumberOfProcessors = KeNumberProcessors;
684 Peb->BeingDebugged = (BOOLEAN)(Process->DebugPort != NULL);
685 Peb->NtGlobalFlag = NtGlobalFlag;
686 /*Peb->HeapSegmentReserve = MmHeapSegmentReserve;
687 Peb->HeapSegmentCommit = MmHeapSegmentCommit;
688 Peb->HeapDeCommitTotalFreeThreshold = MmHeapDeCommitTotalFreeThreshold;
689 Peb->HeapDeCommitFreeBlockThreshold = MmHeapDeCommitFreeBlockThreshold;
690 Peb->CriticalSectionTimeout = MmCriticalSectionTimeout;
691 Peb->MinimumStackCommit = MmMinimumStackCommitInBytes;
692 */
693 Peb->MaximumNumberOfHeaps = (PAGE_SIZE - sizeof(PEB)) / sizeof(PVOID);
694 Peb->ProcessHeaps = (PVOID*)(Peb + 1);
695
696 //
697 // Session ID
698 //
699 MmGetSessionId(Process);
700 }
701 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
702 {
703 //
704 // Fail
705 //
706 KeDetachProcess();
707 _SEH2_YIELD(return _SEH2_GetExceptionCode());
708 }
709 _SEH2_END;
710
711 //
712 // Use SEH in case we can't load the image
713 //
714 _SEH2_TRY
715 {
716 //
717 // Get NT Headers
718 //
719 NtHeaders = RtlImageNtHeader(Peb->ImageBaseAddress);
720 Characteristics = NtHeaders->FileHeader.Characteristics;
721 }
722 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
723 {
724 //
725 // Fail
726 //
727 KeDetachProcess();
728 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT);
729 }
730 _SEH2_END;
731
732 //
733 // Parse the headers
734 //
735 if (NtHeaders)
736 {
737 //
738 // Use SEH in case we can't load the headers
739 //
740 _SEH2_TRY
741 {
742 //
743 // Get the Image Config Data too
744 //
745 ImageConfigData = RtlImageDirectoryEntryToData(Peb->ImageBaseAddress,
746 TRUE,
747 IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
748 (PULONG)&ViewSize);
749 if (ImageConfigData)
750 {
751 //
752 // Probe it
753 //
754 ProbeForRead(ImageConfigData,
755 sizeof(IMAGE_LOAD_CONFIG_DIRECTORY),
756 sizeof(ULONG));
757 }
758
759 //
760 // Write subsystem data
761 //
762 Peb->ImageSubsystem = NtHeaders->OptionalHeader.Subsystem;
763 Peb->ImageSubsystemMajorVersion = NtHeaders->OptionalHeader.MajorSubsystemVersion;
764 Peb->ImageSubsystemMinorVersion = NtHeaders->OptionalHeader.MinorSubsystemVersion;
765
766 //
767 // Check for version data
768 //
769 if (NtHeaders->OptionalHeader.Win32VersionValue)
770 {
771 //
772 // Extract values and write them
773 //
774 Peb->OSMajorVersion = NtHeaders->OptionalHeader.Win32VersionValue & 0xFF;
775 Peb->OSMinorVersion = (NtHeaders->OptionalHeader.Win32VersionValue >> 8) & 0xFF;
776 Peb->OSBuildNumber = (NtHeaders->OptionalHeader.Win32VersionValue >> 16) & 0x3FFF;
777 Peb->OSPlatformId = (NtHeaders->OptionalHeader.Win32VersionValue >> 30) ^ 2;
778
779 /* Process CSD version override */
780 if ((ImageConfigData) && (ImageConfigData->CSDVersion))
781 {
782 /* Take the value from the image configuration directory */
783 Peb->OSCSDVersion = ImageConfigData->CSDVersion;
784 }
785 }
786
787 /* Process optional process affinity mask override */
788 if ((ImageConfigData) && (ImageConfigData->ProcessAffinityMask))
789 {
790 /* Take the value from the image configuration directory */
791 ProcessAffinityMask = ImageConfigData->ProcessAffinityMask;
792 }
793
794 //
795 // Check if this is a UP image
796 if (Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY)
797 {
798 //
799 // Force it to use CPU 0
800 //
801 /* FIXME: this should use the MmRotatingUniprocessorNumber */
802 Peb->ImageProcessAffinityMask = 0;
803 }
804 else
805 {
806 //
807 // Whatever was configured
808 //
809 Peb->ImageProcessAffinityMask = ProcessAffinityMask;
810 }
811 }
812 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
813 {
814 //
815 // Fail
816 //
817 KeDetachProcess();
818 _SEH2_YIELD(return STATUS_INVALID_IMAGE_PROTECT);
819 }
820 _SEH2_END;
821 }
822
823 //
824 // Detach from the Process
825 //
826 KeDetachProcess();
827 *BasePeb = Peb;
828 return STATUS_SUCCESS;
829 }
830
831 NTSTATUS
832 NTAPI
833 MmCreateTeb(IN PEPROCESS Process,
834 IN PCLIENT_ID ClientId,
835 IN PINITIAL_TEB InitialTeb,
836 OUT PTEB *BaseTeb)
837 {
838 PTEB Teb;
839 NTSTATUS Status = STATUS_SUCCESS;
840 *BaseTeb = NULL;
841
842 //
843 // Attach to Target
844 //
845 KeAttachProcess(&Process->Pcb);
846
847 //
848 // Allocate the TEB
849 //
850 Status = MiCreatePebOrTeb(Process, sizeof(TEB), (PULONG_PTR)&Teb);
851 ASSERT(NT_SUCCESS(Status));
852
853 //
854 // Use SEH in case we can't load the TEB
855 //
856 _SEH2_TRY
857 {
858 //
859 // Initialize the PEB
860 //
861 RtlZeroMemory(Teb, sizeof(TEB));
862
863 //
864 // Set TIB Data
865 //
866 #ifdef _M_AMD64
867 Teb->NtTib.ExceptionList = NULL;
868 #else
869 Teb->NtTib.ExceptionList = EXCEPTION_CHAIN_END;
870 #endif
871 Teb->NtTib.Self = (PNT_TIB)Teb;
872
873 //
874 // Identify this as an OS/2 V3.0 ("Cruiser") TIB
875 //
876 Teb->NtTib.Version = 30 << 8;
877
878 //
879 // Set TEB Data
880 //
881 Teb->ClientId = *ClientId;
882 Teb->RealClientId = *ClientId;
883 Teb->ProcessEnvironmentBlock = Process->Peb;
884 Teb->CurrentLocale = PsDefaultThreadLocaleId;
885
886 //
887 // Check if we have a grandparent TEB
888 //
889 if ((InitialTeb->PreviousStackBase == NULL) &&
890 (InitialTeb->PreviousStackLimit == NULL))
891 {
892 //
893 // Use initial TEB values
894 //
895 Teb->NtTib.StackBase = InitialTeb->StackBase;
896 Teb->NtTib.StackLimit = InitialTeb->StackLimit;
897 Teb->DeallocationStack = InitialTeb->AllocatedStackBase;
898 }
899 else
900 {
901 //
902 // Use grandparent TEB values
903 //
904 Teb->NtTib.StackBase = InitialTeb->PreviousStackBase;
905 Teb->NtTib.StackLimit = InitialTeb->PreviousStackLimit;
906 }
907
908 //
909 // Initialize the static unicode string
910 //
911 Teb->StaticUnicodeString.MaximumLength = sizeof(Teb->StaticUnicodeBuffer);
912 Teb->StaticUnicodeString.Buffer = Teb->StaticUnicodeBuffer;
913 }
914 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
915 {
916 //
917 // Get error code
918 //
919 Status = _SEH2_GetExceptionCode();
920 }
921 _SEH2_END;
922
923 //
924 // Return
925 //
926 KeDetachProcess();
927 *BaseTeb = Teb;
928 return Status;
929 }
930
931 VOID
932 NTAPI
933 MiInitializeWorkingSetList(IN PEPROCESS CurrentProcess)
934 {
935 PMMPFN Pfn1;
936 PMMPTE sysPte;
937 MMPTE tempPte;
938
939 /* Setup some bogus list data */
940 MmWorkingSetList->LastEntry = CurrentProcess->Vm.MinimumWorkingSetSize;
941 MmWorkingSetList->HashTable = NULL;
942 MmWorkingSetList->HashTableSize = 0;
943 MmWorkingSetList->NumberOfImageWaiters = 0;
944 MmWorkingSetList->Wsle = (PVOID)0xDEADBABE;
945 MmWorkingSetList->VadBitMapHint = 1;
946 MmWorkingSetList->HashTableStart = (PVOID)0xBADAB00B;
947 MmWorkingSetList->HighestPermittedHashAddress = (PVOID)0xCAFEBABE;
948 MmWorkingSetList->FirstFree = 1;
949 MmWorkingSetList->FirstDynamic = 2;
950 MmWorkingSetList->NextSlot = 3;
951 MmWorkingSetList->LastInitializedWsle = 4;
952
953 /* The rule is that the owner process is always in the FLINK of the PDE's PFN entry */
954 Pfn1 = MiGetPfnEntry(CurrentProcess->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT);
955 ASSERT(Pfn1->u4.PteFrame == MiGetPfnEntryIndex(Pfn1));
956 Pfn1->u1.Event = (PKEVENT)CurrentProcess;
957
958 /* Map the process working set in kernel space */
959 sysPte = MiReserveSystemPtes(1, SystemPteSpace);
960 MI_MAKE_HARDWARE_PTE_KERNEL(&tempPte, sysPte, MM_READWRITE, CurrentProcess->WorkingSetPage);
961 MI_WRITE_VALID_PTE(sysPte, tempPte);
962 CurrentProcess->Vm.VmWorkingSetList = MiPteToAddress(sysPte);
963 }
964
965 NTSTATUS
966 NTAPI
967 MmInitializeProcessAddressSpace(IN PEPROCESS Process,
968 IN PEPROCESS ProcessClone OPTIONAL,
969 IN PVOID Section OPTIONAL,
970 IN OUT PULONG Flags,
971 IN POBJECT_NAME_INFORMATION *AuditName OPTIONAL)
972 {
973 NTSTATUS Status = STATUS_SUCCESS;
974 SIZE_T ViewSize = 0;
975 PVOID ImageBase = 0;
976 PROS_SECTION_OBJECT SectionObject = Section;
977 PMMPTE PointerPte;
978 KIRQL OldIrql;
979 PMMPDE PointerPde;
980 PFN_NUMBER PageFrameNumber;
981 UNICODE_STRING FileName;
982 PWCHAR Source;
983 PCHAR Destination;
984 USHORT Length = 0;
985 MMPTE TempPte;
986
987 /* We should have a PDE */
988 ASSERT(Process->Pcb.DirectoryTableBase[0] != 0);
989 ASSERT(Process->PdeUpdateNeeded == FALSE);
990
991 /* Attach to the process */
992 KeAttachProcess(&Process->Pcb);
993
994 /* The address space should now been in phase 1 or 0 */
995 ASSERT(Process->AddressSpaceInitialized <= 1);
996 Process->AddressSpaceInitialized = 2;
997
998 /* Initialize the Addresss Space lock */
999 KeInitializeGuardedMutex(&Process->AddressCreationLock);
1000 Process->Vm.WorkingSetExpansionLinks.Flink = NULL;
1001
1002 /* Initialize AVL tree */
1003 ASSERT(Process->VadRoot.NumberGenericTableElements == 0);
1004 Process->VadRoot.BalancedRoot.u1.Parent = &Process->VadRoot.BalancedRoot;
1005
1006 /* Lock PFN database */
1007 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1008
1009 /* Setup the PFN for the PDE base of this process */
1010 #ifdef _M_AMD64
1011 PointerPte = MiAddressToPte(PXE_BASE);
1012 #else
1013 PointerPte = MiAddressToPte(PDE_BASE);
1014 #endif
1015 PageFrameNumber = PFN_FROM_PTE(PointerPte);
1016 ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE);
1017 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
1018
1019 /* Do the same for hyperspace */
1020 #ifdef _M_AMD64
1021 PointerPde = MiAddressToPxe((PVOID)HYPER_SPACE);
1022 #else
1023 PointerPde = MiAddressToPde(HYPER_SPACE);
1024 #endif
1025 PageFrameNumber = PFN_FROM_PTE(PointerPde);
1026 //ASSERT(Process->Pcb.DirectoryTableBase[0] == PageFrameNumber * PAGE_SIZE); // we're not lucky
1027 MiInitializePfn(PageFrameNumber, (PMMPTE)PointerPde, TRUE);
1028
1029 /* Setup the PFN for the PTE for the working set */
1030 PointerPte = MiAddressToPte(MI_WORKING_SET_LIST);
1031 MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, MM_READWRITE, 0);
1032 ASSERT(PointerPte->u.Long != 0);
1033 PageFrameNumber = PFN_FROM_PTE(PointerPte);
1034 MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPte);
1035 MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
1036 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
1037 MI_WRITE_VALID_PTE(PointerPte, TempPte);
1038
1039 /* Now initialize the working set list */
1040 MiInitializeWorkingSetList(Process);
1041
1042 /* Sanity check */
1043 ASSERT(Process->PhysicalVadRoot == NULL);
1044
1045 /* Release PFN lock */
1046 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1047
1048 /* Lock the VAD, ARM3-owned ranges away */
1049 MiRosTakeOverSharedUserPage(Process);
1050
1051 /* Check if there's a Section Object */
1052 if (SectionObject)
1053 {
1054 /* Determine the image file name and save it to EPROCESS */
1055 FileName = SectionObject->FileObject->FileName;
1056 Source = (PWCHAR)((PCHAR)FileName.Buffer + FileName.Length);
1057 if (FileName.Buffer)
1058 {
1059 /* Loop the file name*/
1060 while (Source > FileName.Buffer)
1061 {
1062 /* Make sure this isn't a backslash */
1063 if (*--Source == OBJ_NAME_PATH_SEPARATOR)
1064 {
1065 /* If so, stop it here */
1066 Source++;
1067 break;
1068 }
1069 else
1070 {
1071 /* Otherwise, keep going */
1072 Length++;
1073 }
1074 }
1075 }
1076
1077 /* Copy the to the process and truncate it to 15 characters if necessary */
1078 Destination = Process->ImageFileName;
1079 Length = min(Length, sizeof(Process->ImageFileName) - 1);
1080 while (Length--) *Destination++ = (UCHAR)*Source++;
1081 *Destination = ANSI_NULL;
1082
1083 /* Check if caller wants an audit name */
1084 if (AuditName)
1085 {
1086 /* Setup the audit name */
1087 Status = SeInitializeProcessAuditName(SectionObject->FileObject,
1088 FALSE,
1089 AuditName);
1090 if (!NT_SUCCESS(Status))
1091 {
1092 /* Fail */
1093 KeDetachProcess();
1094 return Status;
1095 }
1096 }
1097
1098 /* Map the section */
1099 Status = MmMapViewOfSection(Section,
1100 Process,
1101 (PVOID*)&ImageBase,
1102 0,
1103 0,
1104 NULL,
1105 &ViewSize,
1106 0,
1107 MEM_COMMIT,
1108 PAGE_READWRITE);
1109
1110 /* Save the pointer */
1111 Process->SectionBaseAddress = ImageBase;
1112 }
1113
1114 /* Be nice and detach */
1115 KeDetachProcess();
1116
1117 /* Return status to caller */
1118 return Status;
1119 }
1120
1121 NTSTATUS
1122 NTAPI
1123 INIT_FUNCTION
1124 MmInitializeHandBuiltProcess(IN PEPROCESS Process,
1125 IN PULONG_PTR DirectoryTableBase)
1126 {
1127 /* Share the directory base with the idle process */
1128 DirectoryTableBase[0] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[0];
1129 DirectoryTableBase[1] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[1];
1130
1131 /* Initialize the Addresss Space */
1132 KeInitializeGuardedMutex(&Process->AddressCreationLock);
1133 KeInitializeSpinLock(&Process->HyperSpaceLock);
1134 Process->Vm.WorkingSetExpansionLinks.Flink = NULL;
1135 ASSERT(Process->VadRoot.NumberGenericTableElements == 0);
1136 Process->VadRoot.BalancedRoot.u1.Parent = &Process->VadRoot.BalancedRoot;
1137
1138 /* Use idle process Working set */
1139 Process->Vm.VmWorkingSetList = PsGetCurrentProcess()->Vm.VmWorkingSetList;
1140
1141 /* Done */
1142 Process->HasAddressSpace = TRUE;//??
1143 return STATUS_SUCCESS;
1144 }
1145
1146 NTSTATUS
1147 NTAPI
1148 INIT_FUNCTION
1149 MmInitializeHandBuiltProcess2(IN PEPROCESS Process)
1150 {
1151 /* Lock the VAD, ARM3-owned ranges away */
1152 MiRosTakeOverSharedUserPage(Process);
1153 return STATUS_SUCCESS;
1154 }
1155
1156 #ifdef _M_IX86
1157 /* FIXME: Evaluate ways to make this portable yet arch-specific */
1158 BOOLEAN
1159 NTAPI
1160 MmCreateProcessAddressSpace(IN ULONG MinWs,
1161 IN PEPROCESS Process,
1162 OUT PULONG_PTR DirectoryTableBase)
1163 {
1164 KIRQL OldIrql;
1165 PFN_NUMBER PdeIndex, HyperIndex, WsListIndex;
1166 PMMPTE PointerPte;
1167 MMPTE TempPte, PdePte;
1168 ULONG PdeOffset;
1169 PMMPTE SystemTable, HyperTable;
1170 ULONG Color;
1171 PMMPFN Pfn1;
1172
1173 /* Choose a process color */
1174 Process->NextPageColor = (USHORT)RtlRandom(&MmProcessColorSeed);
1175
1176 /* Setup the hyperspace lock */
1177 KeInitializeSpinLock(&Process->HyperSpaceLock);
1178
1179 /* Lock PFN database */
1180 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1181
1182 /* Get a zero page for the PDE, if possible */
1183 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1184 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY);
1185 PdeIndex = MiRemoveZeroPageSafe(Color);
1186 if (!PdeIndex)
1187 {
1188 /* No zero pages, grab a free one */
1189 PdeIndex = MiRemoveAnyPage(Color);
1190
1191 /* Zero it outside the PFN lock */
1192 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1193 MiZeroPhysicalPage(PdeIndex);
1194 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1195 }
1196
1197 /* Get a zero page for hyperspace, if possible */
1198 MI_SET_USAGE(MI_USAGE_PAGE_DIRECTORY);
1199 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1200 HyperIndex = MiRemoveZeroPageSafe(Color);
1201 if (!HyperIndex)
1202 {
1203 /* No zero pages, grab a free one */
1204 HyperIndex = MiRemoveAnyPage(Color);
1205
1206 /* Zero it outside the PFN lock */
1207 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1208 MiZeroPhysicalPage(HyperIndex);
1209 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1210 }
1211
1212 /* Get a zero page for the woring set list, if possible */
1213 MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
1214 Color = MI_GET_NEXT_PROCESS_COLOR(Process);
1215 WsListIndex = MiRemoveZeroPageSafe(Color);
1216 if (!WsListIndex)
1217 {
1218 /* No zero pages, grab a free one */
1219 WsListIndex = MiRemoveAnyPage(Color);
1220
1221 /* Zero it outside the PFN lock */
1222 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1223 MiZeroPhysicalPage(WsListIndex);
1224 }
1225 else
1226 {
1227 /* Release the PFN lock */
1228 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1229 }
1230
1231 /* Switch to phase 1 initialization */
1232 ASSERT(Process->AddressSpaceInitialized == 0);
1233 Process->AddressSpaceInitialized = 1;
1234
1235 /* Set the base directory pointers */
1236 Process->WorkingSetPage = WsListIndex;
1237 DirectoryTableBase[0] = PdeIndex << PAGE_SHIFT;
1238 DirectoryTableBase[1] = HyperIndex << PAGE_SHIFT;
1239
1240 /* Make sure we don't already have a page directory setup */
1241 ASSERT(Process->Pcb.DirectoryTableBase[0] == 0);
1242
1243 /* Get a PTE to map hyperspace */
1244 PointerPte = MiReserveSystemPtes(1, SystemPteSpace);
1245 ASSERT(PointerPte != NULL);
1246
1247 /* Build it */
1248 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte,
1249 PointerPte,
1250 MM_READWRITE,
1251 HyperIndex);
1252
1253 /* Set it dirty and map it */
1254 MI_MAKE_DIRTY_PAGE(&PdePte);
1255 MI_WRITE_VALID_PTE(PointerPte, PdePte);
1256
1257 /* Now get hyperspace's page table */
1258 HyperTable = MiPteToAddress(PointerPte);
1259
1260 /* Now write the PTE/PDE entry for the working set list index itself */
1261 TempPte = ValidKernelPte;
1262 TempPte.u.Hard.PageFrameNumber = WsListIndex;
1263 /* Hyperspace is local */
1264 MI_MAKE_LOCAL_PAGE(&TempPte);
1265 PdeOffset = MiAddressToPteOffset(MmWorkingSetList);
1266 HyperTable[PdeOffset] = TempPte;
1267
1268 /* Let go of the system PTE */
1269 MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace);
1270
1271 /* Save the PTE address of the page directory itself */
1272 Pfn1 = MiGetPfnEntry(PdeIndex);
1273 Pfn1->PteAddress = (PMMPTE)PDE_BASE;
1274
1275 /* Insert us into the Mm process list */
1276 InsertTailList(&MmProcessList, &Process->MmProcessLinks);
1277
1278 /* Get a PTE to map the page directory */
1279 PointerPte = MiReserveSystemPtes(1, SystemPteSpace);
1280 ASSERT(PointerPte != NULL);
1281
1282 /* Build it */
1283 MI_MAKE_HARDWARE_PTE_KERNEL(&PdePte,
1284 PointerPte,
1285 MM_READWRITE,
1286 PdeIndex);
1287
1288 /* Set it dirty and map it */
1289 MI_MAKE_DIRTY_PAGE(&PdePte);
1290 MI_WRITE_VALID_PTE(PointerPte, PdePte);
1291
1292 /* Now get the page directory (which we'll double map, so call it a page table */
1293 SystemTable = MiPteToAddress(PointerPte);
1294
1295 /* Copy all the kernel mappings */
1296 PdeOffset = MiGetPdeOffset(MmSystemRangeStart);
1297 RtlCopyMemory(&SystemTable[PdeOffset],
1298 MiAddressToPde(MmSystemRangeStart),
1299 PAGE_SIZE - PdeOffset * sizeof(MMPTE));
1300
1301 /* Now write the PTE/PDE entry for hyperspace itself */
1302 TempPte = ValidKernelPte;
1303 TempPte.u.Hard.PageFrameNumber = HyperIndex;
1304 PdeOffset = MiGetPdeOffset(HYPER_SPACE);
1305 SystemTable[PdeOffset] = TempPte;
1306
1307 /* Sanity check */
1308 PdeOffset++;
1309 ASSERT(MiGetPdeOffset(MmHyperSpaceEnd) >= PdeOffset);
1310
1311 /* Now do the x86 trick of making the PDE a page table itself */
1312 PdeOffset = MiGetPdeOffset(PTE_BASE);
1313 TempPte.u.Hard.PageFrameNumber = PdeIndex;
1314 SystemTable[PdeOffset] = TempPte;
1315
1316 /* Let go of the system PTE */
1317 MiReleaseSystemPtes(PointerPte, 1, SystemPteSpace);
1318
1319 /* Add the process to the session */
1320 MiSessionAddProcess(Process);
1321 return TRUE;
1322 }
1323 #endif
1324
1325 VOID
1326 NTAPI
1327 MmCleanProcessAddressSpace(IN PEPROCESS Process)
1328 {
1329 PMMVAD Vad;
1330 PMM_AVL_TABLE VadTree;
1331 PETHREAD Thread = PsGetCurrentThread();
1332
1333 /* Only support this */
1334 ASSERT(Process->AddressSpaceInitialized == 2);
1335
1336 /* Remove from the session */
1337 MiSessionRemoveProcess();
1338
1339 /* Lock the process address space from changes */
1340 MmLockAddressSpace(&Process->Vm);
1341 MiLockProcessWorkingSetUnsafe(Process, Thread);
1342
1343 /* VM is deleted now */
1344 Process->VmDeleted = TRUE;
1345 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
1346
1347 /* Enumerate the VADs */
1348 VadTree = &Process->VadRoot;
1349 while (VadTree->NumberGenericTableElements)
1350 {
1351 /* Grab the current VAD */
1352 Vad = (PMMVAD)VadTree->BalancedRoot.RightChild;
1353
1354 /* Lock the working set */
1355 MiLockProcessWorkingSetUnsafe(Process, Thread);
1356
1357 /* Remove this VAD from the tree */
1358 ASSERT(VadTree->NumberGenericTableElements >= 1);
1359 MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree);
1360
1361 /* Only regular VADs supported for now */
1362 ASSERT(Vad->u.VadFlags.VadType == VadNone);
1363
1364 /* Check if this is a section VAD */
1365 if (!(Vad->u.VadFlags.PrivateMemory) && (Vad->ControlArea))
1366 {
1367 /* Remove the view */
1368 MiRemoveMappedView(Process, Vad);
1369 }
1370 else
1371 {
1372 /* Delete the addresses */
1373 MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
1374 (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
1375 Vad);
1376
1377 /* Release the working set */
1378 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
1379 }
1380
1381 /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */
1382 if (Vad->u.VadFlags.Spare == 1)
1383 {
1384 /* Set a flag so MmDeleteMemoryArea knows to free, but not to remove */
1385 Vad->u.VadFlags.Spare = 2;
1386 continue;
1387 }
1388
1389 /* Free the VAD memory */
1390 ExFreePool(Vad);
1391 }
1392
1393 /* Lock the working set */
1394 MiLockProcessWorkingSetUnsafe(Process, Thread);
1395 ASSERT(Process->CloneRoot == NULL);
1396 ASSERT(Process->PhysicalVadRoot == NULL);
1397
1398 /* Delete the shared user data section */
1399 MiDeleteVirtualAddresses(USER_SHARED_DATA, USER_SHARED_DATA, NULL);
1400
1401 /* Release the working set */
1402 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
1403
1404 /* Release the address space */
1405 MmUnlockAddressSpace(&Process->Vm);
1406 }
1407
1408 VOID
1409 NTAPI
1410 MmDeleteProcessAddressSpace2(IN PEPROCESS Process)
1411 {
1412 PMMPFN Pfn1, Pfn2;
1413 KIRQL OldIrql;
1414 PFN_NUMBER PageFrameIndex;
1415
1416 //ASSERT(Process->CommitCharge == 0);
1417
1418 /* Acquire the PFN lock */
1419 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1420
1421 /* Check for fully initialized process */
1422 if (Process->AddressSpaceInitialized == 2)
1423 {
1424 /* Map the working set page and its page table */
1425 Pfn1 = MiGetPfnEntry(Process->WorkingSetPage);
1426 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
1427
1428 /* Nuke it */
1429 MI_SET_PFN_DELETED(Pfn1);
1430 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
1431 MiDecrementShareCount(Pfn1, Process->WorkingSetPage);
1432 ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
1433 MiReleaseSystemPtes(MiAddressToPte(Process->Vm.VmWorkingSetList), 1, SystemPteSpace);
1434
1435 /* Now map hyperspace and its page table */
1436 PageFrameIndex = Process->Pcb.DirectoryTableBase[1] >> PAGE_SHIFT;
1437 Pfn1 = MiGetPfnEntry(PageFrameIndex);
1438 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame);
1439
1440 /* Nuke it */
1441 MI_SET_PFN_DELETED(Pfn1);
1442 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame);
1443 MiDecrementShareCount(Pfn1, PageFrameIndex);
1444 ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
1445
1446 /* Finally, nuke the PDE itself */
1447 PageFrameIndex = Process->Pcb.DirectoryTableBase[0] >> PAGE_SHIFT;
1448 Pfn1 = MiGetPfnEntry(PageFrameIndex);
1449 MI_SET_PFN_DELETED(Pfn1);
1450 MiDecrementShareCount(Pfn1, PageFrameIndex);
1451 MiDecrementShareCount(Pfn1, PageFrameIndex);
1452
1453 /* Page table is now dead. Bye bye... */
1454 ASSERT((Pfn1->u3.e2.ReferenceCount == 0) || (Pfn1->u3.e1.WriteInProgress));
1455 }
1456 else
1457 {
1458 /* A partly-initialized process should never exit through here */
1459 ASSERT(FALSE);
1460 }
1461
1462 /* Release the PFN lock */
1463 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1464
1465 /* Drop a reference on the session */
1466 if (Process->Session) MiReleaseProcessReferenceToSessionDataPage(Process->Session);
1467
1468 /* Clear out the PDE pages */
1469 Process->Pcb.DirectoryTableBase[0] = 0;
1470 Process->Pcb.DirectoryTableBase[1] = 0;
1471 }
1472
1473 /* SESSION CODE TO MOVE TO SESSION.C ******************************************/
1474
1475 PMM_SESSION_SPACE MmSessionSpace;
1476 PFN_NUMBER MiSessionDataPages, MiSessionTagPages, MiSessionTagSizePages;
1477 PFN_NUMBER MiSessionBigPoolPages, MiSessionCreateCharge;
1478 KGUARDED_MUTEX MiSessionIdMutex;
1479 LONG MmSessionDataPages;
1480 PRTL_BITMAP MiSessionIdBitmap;
1481 volatile LONG MiSessionLeaderExists;
1482
1483 VOID
1484 NTAPI
1485 MiInitializeSessionIds(VOID)
1486 {
1487 ULONG Size, BitmapSize;
1488 PFN_NUMBER TotalPages;
1489
1490 /* Setup the total number of data pages needed for the structure */
1491 TotalPages = MI_SESSION_DATA_PAGES_MAXIMUM;
1492 MiSessionDataPages = ROUND_TO_PAGES(sizeof(MM_SESSION_SPACE)) >> PAGE_SHIFT;
1493 ASSERT(MiSessionDataPages <= MI_SESSION_DATA_PAGES_MAXIMUM - 3);
1494 TotalPages -= MiSessionDataPages;
1495
1496 /* Setup the number of pages needed for session pool tags */
1497 MiSessionTagSizePages = 2;
1498 MiSessionBigPoolPages = 1;
1499 MiSessionTagPages = MiSessionTagSizePages + MiSessionBigPoolPages;
1500 ASSERT(MiSessionTagPages <= TotalPages);
1501 ASSERT(MiSessionTagPages < MI_SESSION_TAG_PAGES_MAXIMUM);
1502
1503 /* Total pages needed for a session (FIXME: Probably different on PAE/x64) */
1504 MiSessionCreateCharge = 1 + MiSessionDataPages + MiSessionTagPages;
1505
1506 /* Initialize the lock */
1507 KeInitializeGuardedMutex(&MiSessionIdMutex);
1508
1509 /* Allocate the bitmap */
1510 Size = MI_INITIAL_SESSION_IDS;
1511 BitmapSize = ((Size + 31) / 32) * sizeof(ULONG);
1512 MiSessionIdBitmap = ExAllocatePoolWithTag(PagedPool,
1513 sizeof(RTL_BITMAP) + BitmapSize,
1514 ' mM');
1515 if (MiSessionIdBitmap)
1516 {
1517 /* Free all the bits */
1518 RtlInitializeBitMap(MiSessionIdBitmap,
1519 (PVOID)(MiSessionIdBitmap + 1),
1520 Size);
1521 RtlClearAllBits(MiSessionIdBitmap);
1522 }
1523 else
1524 {
1525 /* Die if we couldn't allocate the bitmap */
1526 KeBugCheckEx(INSTALL_MORE_MEMORY,
1527 MmNumberOfPhysicalPages,
1528 MmLowestPhysicalPage,
1529 MmHighestPhysicalPage,
1530 0x200);
1531 }
1532 }
1533
1534 VOID
1535 NTAPI
1536 MiSessionLeader(IN PEPROCESS Process)
1537 {
1538 KIRQL OldIrql;
1539
1540 /* Set the flag while under the expansion lock */
1541 OldIrql = KeAcquireQueuedSpinLock(LockQueueExpansionLock);
1542 Process->Vm.Flags.SessionLeader = TRUE;
1543 KeReleaseQueuedSpinLock(LockQueueExpansionLock, OldIrql);
1544 }
1545
1546 ULONG
1547 NTAPI
1548 MmGetSessionId(IN PEPROCESS Process)
1549 {
1550 PMM_SESSION_SPACE SessionGlobal;
1551
1552 /* The session leader is always session zero */
1553 if (Process->Vm.Flags.SessionLeader == 1) return 0;
1554
1555 /* Otherwise, get the session global, and read the session ID from it */
1556 SessionGlobal = (PMM_SESSION_SPACE)Process->Session;
1557 if (!SessionGlobal) return 0;
1558 return SessionGlobal->SessionId;
1559 }
1560
1561 VOID
1562 NTAPI
1563 MiReleaseProcessReferenceToSessionDataPage(IN PMM_SESSION_SPACE SessionGlobal)
1564 {
1565 ULONG i, SessionId;
1566 PMMPTE PointerPte;
1567 PFN_NUMBER PageFrameIndex[MI_SESSION_DATA_PAGES_MAXIMUM];
1568 PMMPFN Pfn1;
1569 KIRQL OldIrql;
1570
1571 /* Is there more than just this reference? If so, bail out */
1572 if (InterlockedDecrement(&SessionGlobal->ProcessReferenceToSession)) return;
1573
1574 /* Get the session ID */
1575 SessionId = SessionGlobal->SessionId;
1576 DPRINT1("Last process in sessino %d going down!!!\n", SessionId);
1577
1578 /* Free the session page tables */
1579 ExFreePool(SessionGlobal->PageTables);
1580 ASSERT(!MI_IS_PHYSICAL_ADDRESS(SessionGlobal));
1581
1582 /* Capture the data page PFNs */
1583 PointerPte = MiAddressToPte(SessionGlobal);
1584 for (i = 0; i < MiSessionDataPages; i++)
1585 {
1586 PageFrameIndex[i] = PFN_FROM_PTE(PointerPte + i);
1587 }
1588
1589 /* Release them */
1590 MiReleaseSystemPtes(PointerPte, MiSessionDataPages, SystemPteSpace);
1591
1592 /* Mark them as deleted */
1593 for (i = 0; i < MiSessionDataPages; i++)
1594 {
1595 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
1596 MI_SET_PFN_DELETED(Pfn1);
1597 }
1598
1599 /* Loop every data page and drop a reference count */
1600 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1601 for (i = 0; i < MiSessionDataPages; i++)
1602 {
1603 /* Sanity check that the page is correct, then decrement it */
1604 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex[i]);
1605 ASSERT(Pfn1->u2.ShareCount == 1);
1606 ASSERT(Pfn1->u3.e2.ReferenceCount == 1);
1607 MiDecrementShareCount(Pfn1, PageFrameIndex[i]);
1608 }
1609
1610 /* Done playing with pages, release the lock */
1611 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1612
1613 /* Decrement the number of data pages */
1614 InterlockedDecrement(&MmSessionDataPages);
1615
1616 /* Free this session ID from the session bitmap */
1617 KeAcquireGuardedMutex(&MiSessionIdMutex);
1618 ASSERT(RtlCheckBit(MiSessionIdBitmap, SessionId));
1619 RtlClearBit(MiSessionIdBitmap, SessionId);
1620 KeReleaseGuardedMutex(&MiSessionIdMutex);
1621 }
1622
1623 VOID
1624 NTAPI
1625 MiSessionRemoveProcess(VOID)
1626 {
1627 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1628
1629 /* If the process isn't already in a session, or if it's the leader... */
1630 if (!(CurrentProcess->Flags & PSF_PROCESS_IN_SESSION_BIT) ||
1631 (CurrentProcess->Vm.Flags.SessionLeader))
1632 {
1633 /* Then there's nothing to do */
1634 return;
1635 }
1636
1637 /* Sanity check */
1638 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
1639
1640 /* Remove the process from the list ,and dereference the session */
1641 RemoveEntryList(&CurrentProcess->SessionProcessLinks);
1642 //MiDereferenceSession();
1643 }
1644
1645 VOID
1646 NTAPI
1647 MiSessionAddProcess(IN PEPROCESS NewProcess)
1648 {
1649 PMM_SESSION_SPACE SessionGlobal;
1650
1651 /* The current process must already be in a session */
1652 if (!(PsGetCurrentProcess()->Flags & PSF_PROCESS_IN_SESSION_BIT)) return;
1653
1654 /* Sanity check */
1655 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
1656
1657 /* Get the global session */
1658 SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
1659
1660 /* Increment counters */
1661 InterlockedIncrement((PLONG)&SessionGlobal->ReferenceCount);
1662 InterlockedIncrement(&SessionGlobal->ResidentProcessCount);
1663 InterlockedIncrement(&SessionGlobal->ProcessReferenceToSession);
1664
1665 /* Set the session pointer */
1666 ASSERT(NewProcess->Session == NULL);
1667 NewProcess->Session = SessionGlobal;
1668
1669 /* Insert it into the process list */
1670 InsertTailList(&SessionGlobal->ProcessList, &NewProcess->SessionProcessLinks);
1671
1672 /* Set the flag */
1673 PspSetProcessFlag(NewProcess, PSF_PROCESS_IN_SESSION_BIT);
1674 }
1675
1676 NTSTATUS
1677 NTAPI
1678 MiSessionInitializeWorkingSetList(VOID)
1679 {
1680 KIRQL OldIrql;
1681 PMMPTE PointerPte, PointerPde;
1682 MMPTE TempPte;
1683 ULONG Color, Index;
1684 PFN_NUMBER PageFrameIndex;
1685 PMM_SESSION_SPACE SessionGlobal;
1686 BOOLEAN AllocatedPageTable;
1687 PMMWSL WorkingSetList;
1688
1689 /* Get pointers to session global and the session working set list */
1690 SessionGlobal = MmSessionSpace->GlobalVirtualAddress;
1691 WorkingSetList = (PMMWSL)MiSessionSpaceWs;
1692
1693 /* Fill out the two pointers */
1694 MmSessionSpace->Vm.VmWorkingSetList = WorkingSetList;
1695 MmSessionSpace->Wsle = (PMMWSLE)WorkingSetList->UsedPageTableEntries;
1696
1697 /* Get the PDE for the working set, and check if it's already allocated */
1698 PointerPde = MiAddressToPde(WorkingSetList);
1699 if (PointerPde->u.Hard.Valid == 1)
1700 {
1701 /* Nope, we'll have to do it */
1702 ASSERT(PointerPde->u.Hard.Global == 0);
1703 AllocatedPageTable = FALSE;
1704 }
1705 else
1706 {
1707 /* Yep, that makes our job easier */
1708 AllocatedPageTable = TRUE;
1709 }
1710
1711 /* Get the PTE for the working set */
1712 PointerPte = MiAddressToPte(WorkingSetList);
1713
1714 /* Initialize the working set lock, and lock the PFN database */
1715 ExInitializePushLock(&SessionGlobal->Vm.WorkingSetMutex);
1716 //MmLockPageableSectionByHandle(ExPageLockHandle);
1717 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1718
1719 /* Check if we need a page table */
1720 if (AllocatedPageTable == TRUE)
1721 {
1722 /* Get a zeroed colored zero page */
1723 Color = MI_GET_NEXT_COLOR();
1724 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1725 if (!PageFrameIndex)
1726 {
1727 /* No zero pages, grab a free one */
1728 PageFrameIndex = MiRemoveAnyPage(Color);
1729
1730 /* Zero it outside the PFN lock */
1731 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1732 MiZeroPhysicalPage(PageFrameIndex);
1733 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1734 }
1735
1736 /* Write a valid PDE for it */
1737 TempPte.u.Long = ValidKernelPdeLocal.u.Long;
1738 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
1739 MI_WRITE_VALID_PTE(PointerPde, TempPte);
1740
1741 /* Add this into the list */
1742 Index = ((ULONG_PTR)WorkingSetList - (ULONG_PTR)MmSessionBase) >> 22;
1743 MmSessionSpace->PageTables[Index] = TempPte;
1744
1745 /* Initialize the page directory page, and now zero the working set list itself */
1746 MiInitializePfnForOtherProcess(PageFrameIndex,
1747 PointerPde,
1748 MmSessionSpace->SessionPageDirectoryIndex);
1749 KeZeroPages(PointerPte, PAGE_SIZE);
1750 }
1751
1752 /* Get a zeroed colored zero page */
1753 Color = MI_GET_NEXT_COLOR();
1754 PageFrameIndex = MiRemoveZeroPageSafe(Color);
1755 if (!PageFrameIndex)
1756 {
1757 /* No zero pages, grab a free one */
1758 PageFrameIndex = MiRemoveAnyPage(Color);
1759
1760 /* Zero it outside the PFN lock */
1761 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1762 MiZeroPhysicalPage(PageFrameIndex);
1763 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1764 }
1765
1766 /* Write a valid PTE for it */
1767 TempPte.u.Long = ValidKernelPteLocal.u.Long;
1768 TempPte.u.Hard.Dirty = TRUE;
1769 TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
1770
1771 /* Initialize the working set list page */
1772 MiInitializePfnAndMakePteValid(PageFrameIndex, PointerPte, TempPte);
1773
1774 /* Now we can release the PFN database lock */
1775 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1776
1777 /* Fill out the working set structure */
1778 MmSessionSpace->Vm.Flags.SessionSpace = 1;
1779 MmSessionSpace->Vm.MinimumWorkingSetSize = 20;
1780 MmSessionSpace->Vm.MaximumWorkingSetSize = 384;
1781 WorkingSetList->LastEntry = 20;
1782 WorkingSetList->HashTable = NULL;
1783 WorkingSetList->HashTableSize = 0;
1784 WorkingSetList->Wsle = MmSessionSpace->Wsle;
1785
1786 /* FIXME: Handle list insertions */
1787 ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
1788 ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
1789 ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Flink == NULL);
1790 ASSERT(SessionGlobal->Vm.WorkingSetExpansionLinks.Blink == NULL);
1791
1792 /* All done, return */
1793 //MmUnlockPageableImageSection(ExPageLockHandle);
1794 return STATUS_SUCCESS;
1795 }
1796
1797 NTSTATUS
1798 NTAPI
1799 MiSessionCreateInternal(OUT PULONG SessionId)
1800 {
1801 PEPROCESS Process = PsGetCurrentProcess();
1802 ULONG NewFlags, Flags, Size, i, Color;
1803 KIRQL OldIrql;
1804 PMMPTE PointerPte, PageTables, SessionPte;
1805 PMMPDE PointerPde;
1806 PMM_SESSION_SPACE SessionGlobal;
1807 MMPTE TempPte;
1808 NTSTATUS Status;
1809 BOOLEAN Result;
1810 PFN_NUMBER SessionPageDirIndex;
1811 PFN_NUMBER TagPage[MI_SESSION_TAG_PAGES_MAXIMUM];
1812 PFN_NUMBER DataPage[MI_SESSION_DATA_PAGES_MAXIMUM];
1813
1814 /* This should not exist yet */
1815 ASSERT(MmIsAddressValid(MmSessionSpace) == FALSE);
1816
1817 /* Loop so we can set the session-is-creating flag */
1818 Flags = Process->Flags;
1819 while (TRUE)
1820 {
1821 /* Check if it's already set */
1822 if (Flags & PSF_SESSION_CREATION_UNDERWAY_BIT)
1823 {
1824 /* Bail out */
1825 DPRINT1("Lost session race\n");
1826 return STATUS_ALREADY_COMMITTED;
1827 }
1828
1829 /* Now try to set it */
1830 NewFlags = InterlockedCompareExchange((PLONG)&Process->Flags,
1831 Flags | PSF_SESSION_CREATION_UNDERWAY_BIT,
1832 Flags);
1833 if (NewFlags == Flags) break;
1834
1835 /* It changed, try again */
1836 Flags = NewFlags;
1837 }
1838
1839 /* Now we should own the flag */
1840 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
1841
1842 /*
1843 * Session space covers everything from 0xA0000000 to 0xC0000000.
1844 * Allocate enough page tables to describe the entire region
1845 */
1846 Size = (0x20000000 / PDE_MAPPED_VA) * sizeof(MMPTE);
1847 PageTables = ExAllocatePoolWithTag(NonPagedPool, Size, 'tHmM');
1848 ASSERT(PageTables != NULL);
1849 RtlZeroMemory(PageTables, Size);
1850
1851 /* Lock the session ID creation mutex */
1852 KeAcquireGuardedMutex(&MiSessionIdMutex);
1853
1854 /* Allocate a new Session ID */
1855 *SessionId = RtlFindClearBitsAndSet(MiSessionIdBitmap, 1, 0);
1856 if (*SessionId == 0xFFFFFFFF)
1857 {
1858 /* We ran out of session IDs, we should expand */
1859 DPRINT1("Too many sessions created. Expansion not yet supported\n");
1860 return STATUS_NO_MEMORY;
1861 }
1862
1863 /* Unlock the session ID creation mutex */
1864 KeReleaseGuardedMutex(&MiSessionIdMutex);
1865
1866 /* Reserve the global PTEs */
1867 SessionPte = MiReserveSystemPtes(MiSessionDataPages, SystemPteSpace);
1868 ASSERT(SessionPte != NULL);
1869
1870 /* Acquire the PFN lock while we set everything up */
1871 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1872
1873 /* Loop the global PTEs */
1874 TempPte.u.Long = ValidKernelPte.u.Long;
1875 for (i = 0; i < MiSessionDataPages; i++)
1876 {
1877 /* Get a zeroed colored zero page */
1878 Color = MI_GET_NEXT_COLOR();
1879 DataPage[i] = MiRemoveZeroPageSafe(Color);
1880 if (!DataPage[i])
1881 {
1882 /* No zero pages, grab a free one */
1883 DataPage[i] = MiRemoveAnyPage(Color);
1884
1885 /* Zero it outside the PFN lock */
1886 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1887 MiZeroPhysicalPage(DataPage[i]);
1888 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1889 }
1890
1891 /* Fill the PTE out */
1892 TempPte.u.Hard.PageFrameNumber = DataPage[i];
1893 MI_WRITE_VALID_PTE(SessionPte + i, TempPte);
1894 }
1895
1896 /* Set the pointer to global space */
1897 SessionGlobal = MiPteToAddress(SessionPte);
1898
1899 /* Get a zeroed colored zero page */
1900 Color = MI_GET_NEXT_COLOR();
1901 SessionPageDirIndex = MiRemoveZeroPageSafe(Color);
1902 if (!SessionPageDirIndex)
1903 {
1904 /* No zero pages, grab a free one */
1905 SessionPageDirIndex = MiRemoveAnyPage(Color);
1906
1907 /* Zero it outside the PFN lock */
1908 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1909 MiZeroPhysicalPage(SessionPageDirIndex);
1910 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1911 }
1912
1913 /* Fill the PTE out */
1914 TempPte.u.Long = ValidKernelPdeLocal.u.Long;
1915 TempPte.u.Hard.PageFrameNumber = SessionPageDirIndex;
1916
1917 /* Setup, allocate, fill out the MmSessionSpace PTE */
1918 PointerPde = MiAddressToPde(MmSessionSpace);
1919 ASSERT(PointerPde->u.Long == 0);
1920 MI_WRITE_VALID_PTE(PointerPde, TempPte);
1921 MiInitializePfnForOtherProcess(SessionPageDirIndex,
1922 PointerPde,
1923 SessionPageDirIndex);
1924 ASSERT(MI_PFN_ELEMENT(SessionPageDirIndex)->u1.WsIndex == 0);
1925
1926 /* Loop all the local PTEs for it */
1927 TempPte.u.Long = ValidKernelPteLocal.u.Long;
1928 PointerPte = MiAddressToPte(MmSessionSpace);
1929 for (i = 0; i < MiSessionDataPages; i++)
1930 {
1931 /* And fill them out */
1932 TempPte.u.Hard.PageFrameNumber = DataPage[i];
1933 MiInitializePfnAndMakePteValid(DataPage[i], PointerPte + i, TempPte);
1934 ASSERT(MI_PFN_ELEMENT(DataPage[i])->u1.WsIndex == 0);
1935 }
1936
1937 /* Finally loop all of the session pool tag pages */
1938 for (i = 0; i < MiSessionTagPages; i++)
1939 {
1940 /* Grab a zeroed colored page */
1941 Color = MI_GET_NEXT_COLOR();
1942 TagPage[i] = MiRemoveZeroPageSafe(Color);
1943 if (!TagPage[i])
1944 {
1945 /* No zero pages, grab a free one */
1946 TagPage[i] = MiRemoveAnyPage(Color);
1947
1948 /* Zero it outside the PFN lock */
1949 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1950 MiZeroPhysicalPage(TagPage[i]);
1951 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1952 }
1953
1954 /* Fill the PTE out */
1955 TempPte.u.Hard.PageFrameNumber = TagPage[i];
1956 MiInitializePfnAndMakePteValid(TagPage[i],
1957 PointerPte + MiSessionDataPages + i,
1958 TempPte);
1959 }
1960
1961 /* PTEs have been setup, release the PFN lock */
1962 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1963
1964 /* Fill out the session space structure now */
1965 MmSessionSpace->GlobalVirtualAddress = SessionGlobal;
1966 MmSessionSpace->ReferenceCount = 1;
1967 MmSessionSpace->ResidentProcessCount = 1;
1968 MmSessionSpace->u.LongFlags = 0;
1969 MmSessionSpace->SessionId = *SessionId;
1970 MmSessionSpace->LocaleId = PsDefaultSystemLocaleId;
1971 MmSessionSpace->SessionPageDirectoryIndex = SessionPageDirIndex;
1972 MmSessionSpace->Color = Color;
1973 MmSessionSpace->NonPageablePages = MiSessionCreateCharge;
1974 MmSessionSpace->CommittedPages = MiSessionCreateCharge;
1975 MmSessionSpace->PageTables = PageTables;
1976 MmSessionSpace->PageTables[PointerPde - MiAddressToPde(MmSessionBase)] = *PointerPde;
1977 InitializeListHead(&MmSessionSpace->ImageList);
1978 DPRINT1("Session %d is ready to go: 0x%p 0x%p, %lx 0x%p\n",
1979 *SessionId, MmSessionSpace, SessionGlobal, SessionPageDirIndex, PageTables);
1980
1981 /* Initialize session pool */
1982 //Status = MiInitializeSessionPool();
1983 Status = STATUS_SUCCESS;
1984 ASSERT(NT_SUCCESS(Status) == TRUE);
1985
1986 /* Initialize system space */
1987 Result = MiInitializeSystemSpaceMap(&SessionGlobal->Session);
1988 ASSERT(Result == TRUE);
1989
1990 /* Initialize the process list, make sure the workign set list is empty */
1991 ASSERT(SessionGlobal->WsListEntry.Flink == NULL);
1992 ASSERT(SessionGlobal->WsListEntry.Blink == NULL);
1993 InitializeListHead(&SessionGlobal->ProcessList);
1994
1995 /* We're done, clear the flag */
1996 ASSERT(Process->Flags & PSF_SESSION_CREATION_UNDERWAY_BIT);
1997 PspClearProcessFlag(Process, PSF_SESSION_CREATION_UNDERWAY_BIT);
1998
1999 /* Insert the process into the session */
2000 ASSERT(Process->Session == NULL);
2001 ASSERT(SessionGlobal->ProcessReferenceToSession == 0);
2002 SessionGlobal->ProcessReferenceToSession = 1;
2003
2004 /* We're done */
2005 InterlockedIncrement(&MmSessionDataPages);
2006 return STATUS_SUCCESS;
2007 }
2008
2009 NTSTATUS
2010 NTAPI
2011 MmSessionCreate(OUT PULONG SessionId)
2012 {
2013 PEPROCESS Process = PsGetCurrentProcess();
2014 ULONG SessionLeaderExists;
2015 NTSTATUS Status;
2016
2017 /* Fail if the process is already in a session */
2018 if (Process->Flags & PSF_PROCESS_IN_SESSION_BIT)
2019 {
2020 DPRINT1("Process already in session\n");
2021 return STATUS_ALREADY_COMMITTED;
2022 }
2023
2024 /* Check if the process is already the session leader */
2025 if (!Process->Vm.Flags.SessionLeader)
2026 {
2027 /* Atomically set it as the leader */
2028 SessionLeaderExists = InterlockedCompareExchange(&MiSessionLeaderExists, 1, 0);
2029 if (SessionLeaderExists)
2030 {
2031 DPRINT1("Session leader race\n");
2032 return STATUS_INVALID_SYSTEM_SERVICE;
2033 }
2034
2035 /* Do the work required to upgrade him */
2036 MiSessionLeader(Process);
2037 }
2038
2039 /* Create the session */
2040 KeEnterCriticalRegion();
2041 Status = MiSessionCreateInternal(SessionId);
2042 if (!NT_SUCCESS(Status))
2043 {
2044 KeLeaveCriticalRegion();
2045 return Status;
2046 }
2047
2048 /* Set up the session working set */
2049 Status = MiSessionInitializeWorkingSetList();
2050 if (!NT_SUCCESS(Status))
2051 {
2052 /* Fail */
2053 //MiDereferenceSession();
2054 ASSERT(FALSE);
2055 KeLeaveCriticalRegion();
2056 return Status;
2057 }
2058
2059 /* All done */
2060 KeLeaveCriticalRegion();
2061
2062 /* Set and assert the flags, and return */
2063 MmSessionSpace->u.Flags.Initialized = 1;
2064 PspSetProcessFlag(Process, PSF_PROCESS_IN_SESSION_BIT);
2065 ASSERT(MiSessionLeaderExists == 1);
2066 return Status;
2067 }
2068
2069 NTSTATUS
2070 NTAPI
2071 MmSessionDelete(IN ULONG SessionId)
2072 {
2073 PEPROCESS Process = PsGetCurrentProcess();
2074
2075 /* Process must be in a session */
2076 if (!(Process->Flags & PSF_PROCESS_IN_SESSION_BIT))
2077 {
2078 DPRINT1("Not in a session!\n");
2079 return STATUS_UNABLE_TO_FREE_VM;
2080 }
2081
2082 /* It must be the session leader */
2083 if (!Process->Vm.Flags.SessionLeader)
2084 {
2085 DPRINT1("Not a session leader!\n");
2086 return STATUS_UNABLE_TO_FREE_VM;
2087 }
2088
2089 /* Remove one reference count */
2090 KeEnterCriticalRegion();
2091 /* FIXME: Do it */
2092 KeLeaveCriticalRegion();
2093
2094 /* All done */
2095 return STATUS_SUCCESS;
2096 }
2097
2098 /* SYSTEM CALLS ***************************************************************/
2099
2100 NTSTATUS
2101 NTAPI
2102 NtAllocateUserPhysicalPages(IN HANDLE ProcessHandle,
2103 IN OUT PULONG_PTR NumberOfPages,
2104 IN OUT PULONG_PTR UserPfnArray)
2105 {
2106 UNIMPLEMENTED;
2107 return STATUS_NOT_IMPLEMENTED;
2108 }
2109
2110 NTSTATUS
2111 NTAPI
2112 NtMapUserPhysicalPages(IN PVOID VirtualAddresses,
2113 IN ULONG_PTR NumberOfPages,
2114 IN OUT PULONG_PTR UserPfnArray)
2115 {
2116 UNIMPLEMENTED;
2117 return STATUS_NOT_IMPLEMENTED;
2118 }
2119
2120 NTSTATUS
2121 NTAPI
2122 NtMapUserPhysicalPagesScatter(IN PVOID *VirtualAddresses,
2123 IN ULONG_PTR NumberOfPages,
2124 IN OUT PULONG_PTR UserPfnArray)
2125 {
2126 UNIMPLEMENTED;
2127 return STATUS_NOT_IMPLEMENTED;
2128 }
2129
2130 NTSTATUS
2131 NTAPI
2132 NtFreeUserPhysicalPages(IN HANDLE ProcessHandle,
2133 IN OUT PULONG_PTR NumberOfPages,
2134 IN OUT PULONG_PTR UserPfnArray)
2135 {
2136 UNIMPLEMENTED;
2137 return STATUS_NOT_IMPLEMENTED;
2138 }
2139
2140 /* EOF */