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