[NTOS:MM]
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / section.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/section.c
5 * PURPOSE: ARM Memory Manager Section Support
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 <mm/ARM3/miarm.h>
17
18 /* GLOBALS ********************************************************************/
19
20 ACCESS_MASK MmMakeSectionAccess[8] =
21 {
22 SECTION_MAP_READ,
23 SECTION_MAP_READ,
24 SECTION_MAP_EXECUTE,
25 SECTION_MAP_EXECUTE | SECTION_MAP_READ,
26 SECTION_MAP_WRITE,
27 SECTION_MAP_READ,
28 SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
29 SECTION_MAP_EXECUTE | SECTION_MAP_READ
30 };
31
32 ACCESS_MASK MmMakeFileAccess[8] =
33 {
34 FILE_READ_DATA,
35 FILE_READ_DATA,
36 FILE_EXECUTE,
37 FILE_EXECUTE | FILE_READ_DATA,
38 FILE_WRITE_DATA | FILE_READ_DATA,
39 FILE_READ_DATA,
40 FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA,
41 FILE_EXECUTE | FILE_READ_DATA
42 };
43
44 CHAR MmUserProtectionToMask1[16] =
45 {
46 0,
47 MM_NOACCESS,
48 MM_READONLY,
49 (CHAR)MM_INVALID_PROTECTION,
50 MM_READWRITE,
51 (CHAR)MM_INVALID_PROTECTION,
52 (CHAR)MM_INVALID_PROTECTION,
53 (CHAR)MM_INVALID_PROTECTION,
54 MM_WRITECOPY,
55 (CHAR)MM_INVALID_PROTECTION,
56 (CHAR)MM_INVALID_PROTECTION,
57 (CHAR)MM_INVALID_PROTECTION,
58 (CHAR)MM_INVALID_PROTECTION,
59 (CHAR)MM_INVALID_PROTECTION,
60 (CHAR)MM_INVALID_PROTECTION,
61 (CHAR)MM_INVALID_PROTECTION
62 };
63
64 CHAR MmUserProtectionToMask2[16] =
65 {
66 0,
67 MM_EXECUTE,
68 MM_EXECUTE_READ,
69 (CHAR)MM_INVALID_PROTECTION,
70 MM_EXECUTE_READWRITE,
71 (CHAR)MM_INVALID_PROTECTION,
72 (CHAR)MM_INVALID_PROTECTION,
73 (CHAR)MM_INVALID_PROTECTION,
74 MM_EXECUTE_WRITECOPY,
75 (CHAR)MM_INVALID_PROTECTION,
76 (CHAR)MM_INVALID_PROTECTION,
77 (CHAR)MM_INVALID_PROTECTION,
78 (CHAR)MM_INVALID_PROTECTION,
79 (CHAR)MM_INVALID_PROTECTION,
80 (CHAR)MM_INVALID_PROTECTION,
81 (CHAR)MM_INVALID_PROTECTION
82 };
83
84 ULONG MmCompatibleProtectionMask[8] =
85 {
86 PAGE_NOACCESS,
87
88 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
89
90 PAGE_NOACCESS | PAGE_EXECUTE,
91
92 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
93 PAGE_EXECUTE_READ,
94
95 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE,
96
97 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
98
99 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE |
100 PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE |
101 PAGE_EXECUTE_WRITECOPY,
102
103 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
104 PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
105 };
106
107 MMSESSION MmSession;
108 KGUARDED_MUTEX MmSectionCommitMutex;
109 MM_AVL_TABLE MmSectionBasedRoot;
110 KGUARDED_MUTEX MmSectionBasedMutex;
111 PVOID MmHighSectionBase;
112
113 /* PRIVATE FUNCTIONS **********************************************************/
114
115 BOOLEAN
116 NTAPI
117 MiIsProtectionCompatible(IN ULONG SectionPageProtection,
118 IN ULONG NewSectionPageProtection)
119 {
120 ULONG ProtectionMask, CompatibleMask;
121
122 /* Calculate the protection mask and make sure it's valid */
123 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
124 if (ProtectionMask == MM_INVALID_PROTECTION)
125 {
126 DPRINT1("Invalid protection mask\n");
127 return FALSE;
128 }
129
130 /* Calculate the compatible mask */
131 CompatibleMask = MmCompatibleProtectionMask[ProtectionMask & 0x7] |
132 PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE;
133
134 /* See if the mapping protection is compatible with the create protection */
135 return ((CompatibleMask | NewSectionPageProtection) == CompatibleMask);
136 }
137
138 ACCESS_MASK
139 NTAPI
140 MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection)
141 {
142 ULONG ProtectionMask;
143
144 /* Calculate the protection mask and make sure it's valid */
145 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
146 if (ProtectionMask == MM_INVALID_PROTECTION)
147 {
148 DPRINT1("Invalid protection mask\n");
149 return STATUS_INVALID_PAGE_PROTECTION;
150 }
151
152 /* Now convert it to the required file access */
153 return MmMakeFileAccess[ProtectionMask & 0x7];
154 }
155
156 ULONG
157 NTAPI
158 MiMakeProtectionMask(IN ULONG Protect)
159 {
160 ULONG Mask1, Mask2, ProtectMask;
161
162 /* PAGE_EXECUTE_WRITECOMBINE is theoretically the maximum */
163 if (Protect >= (PAGE_WRITECOMBINE * 2)) return MM_INVALID_PROTECTION;
164
165 /*
166 * Windows API protection mask can be understood as two bitfields, differing
167 * by whether or not execute rights are being requested
168 */
169 Mask1 = Protect & 0xF;
170 Mask2 = (Protect >> 4) & 0xF;
171
172 /* Check which field is there */
173 if (!Mask1)
174 {
175 /* Mask2 must be there, use it to determine the PTE protection */
176 if (!Mask2) return MM_INVALID_PROTECTION;
177 ProtectMask = MmUserProtectionToMask2[Mask2];
178 }
179 else
180 {
181 /* Mask2 should not be there, use Mask1 to determine the PTE mask */
182 if (Mask2) return MM_INVALID_PROTECTION;
183 ProtectMask = MmUserProtectionToMask1[Mask1];
184 }
185
186 /* Make sure the final mask is a valid one */
187 if (ProtectMask == MM_INVALID_PROTECTION) return MM_INVALID_PROTECTION;
188
189 /* Check for PAGE_GUARD option */
190 if (Protect & PAGE_GUARD)
191 {
192 /* It's not valid on no-access, nocache, or writecombine pages */
193 if ((ProtectMask == MM_NOACCESS) ||
194 (Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)))
195 {
196 /* Fail such requests */
197 return MM_INVALID_PROTECTION;
198 }
199
200 /* This actually turns on guard page in this scenario! */
201 ProtectMask |= MM_GUARDPAGE;
202 }
203
204 /* Check for nocache option */
205 if (Protect & PAGE_NOCACHE)
206 {
207 /* The earlier check should've eliminated this possibility */
208 ASSERT((Protect & PAGE_GUARD) == 0);
209
210 /* Check for no-access page or write combine page */
211 if ((ProtectMask == MM_NOACCESS) || (Protect & PAGE_WRITECOMBINE))
212 {
213 /* Such a request is invalid */
214 return MM_INVALID_PROTECTION;
215 }
216
217 /* Add the PTE flag */
218 ProtectMask |= MM_NOCACHE;
219 }
220
221 /* Check for write combine option */
222 if (Protect & PAGE_WRITECOMBINE)
223 {
224 /* The two earlier scenarios should've caught this */
225 ASSERT((Protect & (PAGE_GUARD | PAGE_NOACCESS)) == 0);
226
227 /* Don't allow on no-access pages */
228 if (ProtectMask == MM_NOACCESS) return MM_INVALID_PROTECTION;
229
230 /* This actually turns on write-combine in this scenario! */
231 ProtectMask |= MM_NOACCESS;
232 }
233
234 /* Return the final MM PTE protection mask */
235 return ProtectMask;
236 }
237
238 BOOLEAN
239 NTAPI
240 MiInitializeSystemSpaceMap(IN PMMSESSION InputSession OPTIONAL)
241 {
242 SIZE_T AllocSize, BitmapSize, Size;
243 PVOID ViewStart;
244 PMMSESSION Session;
245
246 /* Check if this a session or system space */
247 if (InputSession)
248 {
249 /* Use the input session */
250 Session = InputSession;
251 ViewStart = MiSessionViewStart;
252 Size = MmSessionViewSize;
253 }
254 else
255 {
256 /* Use the system space "session" */
257 Session = &MmSession;
258 ViewStart = MiSystemViewStart;
259 Size = MmSystemViewSize;
260 }
261
262 /* Initialize the system space lock */
263 Session->SystemSpaceViewLockPointer = &Session->SystemSpaceViewLock;
264 KeInitializeGuardedMutex(Session->SystemSpaceViewLockPointer);
265
266 /* Set the start address */
267 Session->SystemSpaceViewStart = ViewStart;
268
269 /* Create a bitmap to describe system space */
270 BitmapSize = sizeof(RTL_BITMAP) + ((((Size / MI_SYSTEM_VIEW_BUCKET_SIZE) + 31) / 32) * sizeof(ULONG));
271 Session->SystemSpaceBitMap = ExAllocatePoolWithTag(NonPagedPool,
272 BitmapSize,
273 TAG_MM);
274 ASSERT(Session->SystemSpaceBitMap);
275 RtlInitializeBitMap(Session->SystemSpaceBitMap,
276 (PULONG)(Session->SystemSpaceBitMap + 1),
277 (ULONG)(Size / MI_SYSTEM_VIEW_BUCKET_SIZE));
278
279 /* Set system space fully empty to begin with */
280 RtlClearAllBits(Session->SystemSpaceBitMap);
281
282 /* Set default hash flags */
283 Session->SystemSpaceHashSize = 31;
284 Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
285 Session->SystemSpaceHashEntries = 0;
286
287 /* Calculate how much space for the hash views we'll need */
288 AllocSize = sizeof(MMVIEW) * Session->SystemSpaceHashSize;
289 ASSERT(AllocSize < PAGE_SIZE);
290
291 /* Allocate and zero the view table */
292 Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session == &MmSession ?
293 NonPagedPool :
294 PagedPool,
295 AllocSize,
296 TAG_MM);
297 ASSERT(Session->SystemSpaceViewTable != NULL);
298 RtlZeroMemory(Session->SystemSpaceViewTable, AllocSize);
299
300 /* Success */
301 return TRUE;
302 }
303
304 PVOID
305 NTAPI
306 MiInsertInSystemSpace(IN PMMSESSION Session,
307 IN ULONG Buckets,
308 IN PCONTROL_AREA ControlArea)
309 {
310 PVOID Base;
311 ULONG Entry, Hash, i, HashSize;
312 PMMVIEW OldTable;
313 PAGED_CODE();
314
315 /* Stay within 4GB */
316 ASSERT(Buckets < MI_SYSTEM_VIEW_BUCKET_SIZE);
317
318 /* Lock system space */
319 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
320
321 /* Check if we're going to exhaust hash entries */
322 if ((Session->SystemSpaceHashEntries + 8) > Session->SystemSpaceHashSize)
323 {
324 /* Double the hash size */
325 HashSize = Session->SystemSpaceHashSize * 2;
326
327 /* Save the old table and allocate a new one */
328 OldTable = Session->SystemSpaceViewTable;
329 Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session ==
330 &MmSession ?
331 NonPagedPool :
332 PagedPool,
333 HashSize *
334 sizeof(MMVIEW),
335 TAG_MM);
336 if (!Session->SystemSpaceViewTable)
337 {
338 /* Failed to allocate a new table, keep the old one for now */
339 Session->SystemSpaceViewTable = OldTable;
340 }
341 else
342 {
343 /* Clear the new table and set the new ahsh and key */
344 RtlZeroMemory(Session->SystemSpaceViewTable, HashSize * sizeof(MMVIEW));
345 Session->SystemSpaceHashSize = HashSize;
346 Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
347
348 /* Loop the old table */
349 for (i = 0; i < Session->SystemSpaceHashSize / 2; i++)
350 {
351 /* Check if the entry was valid */
352 if (OldTable[i].Entry)
353 {
354 /* Re-hash the old entry and search for space in the new table */
355 Hash = (OldTable[i].Entry >> 16) % Session->SystemSpaceHashKey;
356 while (Session->SystemSpaceViewTable[Hash].Entry)
357 {
358 /* Loop back at the beginning if we had an overflow */
359 if (++Hash >= Session->SystemSpaceHashSize) Hash = 0;
360 }
361
362 /* Write the old entry in the new table */
363 Session->SystemSpaceViewTable[Hash] = OldTable[i];
364 }
365 }
366
367 /* Free the old table */
368 ExFreePool(OldTable);
369 }
370 }
371
372 /* Check if we ran out */
373 if (Session->SystemSpaceHashEntries == Session->SystemSpaceHashSize)
374 {
375 DPRINT1("Ran out of system view hash entries\n");
376 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
377 return NULL;
378 }
379
380 /* Find space where to map this view */
381 i = RtlFindClearBitsAndSet(Session->SystemSpaceBitMap, Buckets, 0);
382 if (i == 0xFFFFFFFF)
383 {
384 /* Out of space, fail */
385 Session->BitmapFailures++;
386 DPRINT1("Out of system view space\n");
387 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
388 return NULL;
389 }
390
391 /* Compute the base address */
392 Base = (PVOID)((ULONG_PTR)Session->SystemSpaceViewStart + (i * MI_SYSTEM_VIEW_BUCKET_SIZE));
393
394 /* Get the hash entry for this allocation */
395 Entry = ((ULONG_PTR)Base & ~(MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) + Buckets;
396 Hash = (Entry >> 16) % Session->SystemSpaceHashKey;
397
398 /* Loop hash entries until a free one is found */
399 while (Session->SystemSpaceViewTable[Hash].Entry)
400 {
401 /* Unless we overflow, in which case loop back at hash o */
402 if (++Hash >= Session->SystemSpaceHashSize) Hash = 0;
403 }
404
405 /* Add this entry into the hash table */
406 Session->SystemSpaceViewTable[Hash].Entry = Entry;
407 Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea;
408
409 /* Hash entry found, increment total and return the base address */
410 Session->SystemSpaceHashEntries++;
411 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
412 return Base;
413 }
414
415 NTSTATUS
416 NTAPI
417 MiAddMappedPtes(IN PMMPTE FirstPte,
418 IN PFN_NUMBER PteCount,
419 IN PCONTROL_AREA ControlArea)
420 {
421 MMPTE TempPte;
422 PMMPTE PointerPte, ProtoPte, LastProtoPte, LastPte;
423 PSUBSECTION Subsection;
424
425 /* ARM3 doesn't support this yet */
426 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
427 ASSERT(ControlArea->u.Flags.Rom == 0);
428 ASSERT(ControlArea->FilePointer == NULL);
429
430 /* Sanity checks */
431 ASSERT(PteCount != 0);
432 ASSERT(ControlArea->NumberOfMappedViews >= 1);
433 ASSERT(ControlArea->NumberOfUserReferences >= 1);
434 ASSERT(ControlArea->NumberOfSectionReferences != 0);
435 ASSERT(ControlArea->u.Flags.BeingCreated == 0);
436 ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
437 ASSERT(ControlArea->u.Flags.BeingPurged == 0);
438
439 /* Get the PTEs for the actual mapping */
440 PointerPte = FirstPte;
441 LastPte = FirstPte + PteCount;
442
443 /* Get the prototype PTEs that desribe the section mapping in the subsection */
444 Subsection = (PSUBSECTION)(ControlArea + 1);
445 ProtoPte = Subsection->SubsectionBase;
446 LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
447
448 /* Loop the PTEs for the mapping */
449 while (PointerPte < LastPte)
450 {
451 /* We may have run out of prototype PTEs in this subsection */
452 if (ProtoPte >= LastProtoPte)
453 {
454 /* But we don't handle this yet */
455 ASSERT(FALSE);
456 }
457
458 /* The PTE should be completely clear */
459 ASSERT(PointerPte->u.Long == 0);
460
461 /* Build the prototype PTE and write it */
462 MI_MAKE_PROTOTYPE_PTE(&TempPte, ProtoPte);
463 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
464
465 /* Keep going */
466 PointerPte++;
467 ProtoPte++;
468 }
469
470 /* No failure path */
471 return STATUS_SUCCESS;
472 }
473
474 VOID
475 NTAPI
476 MiFillSystemPageDirectory(IN PVOID Base,
477 IN SIZE_T NumberOfBytes)
478 {
479 PMMPDE PointerPde, LastPde, SystemMapPde;
480 MMPDE TempPde;
481 PFN_NUMBER PageFrameIndex, ParentPage;
482 KIRQL OldIrql;
483 PAGED_CODE();
484
485 /* Find the PDEs needed for this mapping */
486 PointerPde = MiAddressToPde(Base);
487 LastPde = MiAddressToPde((PVOID)((ULONG_PTR)Base + NumberOfBytes - 1));
488
489 #if (_MI_PAGING_LEVELS == 2)
490 /* Find the system double-mapped PDE that describes this mapping */
491 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
492 #else
493 /* We don't have a double mapping */
494 SystemMapPde = PointerPde;
495 #endif
496
497 /* Use the PDE template and loop the PDEs */
498 TempPde = ValidKernelPde;
499 while (PointerPde <= LastPde)
500 {
501 /* Lock the PFN database */
502 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
503
504 /* Check if we don't already have this PDE mapped */
505 if (SystemMapPde->u.Hard.Valid == 0)
506 {
507 /* Grab a page for it */
508 MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
509 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
510 PageFrameIndex = MiRemoveZeroPage(MI_GET_NEXT_COLOR());
511 ASSERT(PageFrameIndex);
512 TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
513
514 #if (_MI_PAGING_LEVELS == 2)
515 ParentPage = MmSystemPageDirectory[(PointerPde - MiAddressToPde(NULL)) / PDE_COUNT];
516 #else
517 ParentPage = MiPdeToPpe(PointerPde)->u.Hard.PageFrameNumber;
518 #endif
519 /* Initialize its PFN entry, with the parent system page directory page table */
520 MiInitializePfnForOtherProcess(PageFrameIndex,
521 (PMMPTE)PointerPde,
522 ParentPage);
523
524 /* Make the system PDE entry valid */
525 MI_WRITE_VALID_PDE(SystemMapPde, TempPde);
526
527 /* The system PDE entry might be the PDE itself, so check for this */
528 if (PointerPde->u.Hard.Valid == 0)
529 {
530 /* It's different, so make the real PDE valid too */
531 MI_WRITE_VALID_PDE(PointerPde, TempPde);
532 }
533 }
534
535 /* Release the lock and keep going with the next PDE */
536 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
537 SystemMapPde++;
538 PointerPde++;
539 }
540 }
541
542 NTSTATUS
543 NTAPI
544 MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea,
545 IN BOOLEAN FailIfSystemViews)
546 {
547 KIRQL OldIrql;
548
549 /* Flag not yet supported */
550 ASSERT(FailIfSystemViews == FALSE);
551
552 /* Lock the PFN database */
553 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
554
555 /* State not yet supported */
556 ASSERT(ControlArea->u.Flags.BeingPurged == 0);
557
558 /* Increase the reference counts */
559 ControlArea->NumberOfMappedViews++;
560 ControlArea->NumberOfUserReferences++;
561 ASSERT(ControlArea->NumberOfSectionReferences != 0);
562
563 /* Release the PFN lock and return success */
564 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
565 return STATUS_SUCCESS;
566 }
567
568 PSUBSECTION
569 NTAPI
570 MiLocateSubsection(IN PMMVAD Vad,
571 IN ULONG_PTR Vpn)
572 {
573 PSUBSECTION Subsection;
574 PCONTROL_AREA ControlArea;
575 ULONG_PTR PteOffset;
576
577 /* Get the control area */
578 ControlArea = Vad->ControlArea;
579 ASSERT(ControlArea->u.Flags.Rom == 0);
580 ASSERT(ControlArea->u.Flags.Image == 0);
581 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
582
583 /* Get the subsection */
584 Subsection = (PSUBSECTION)(ControlArea + 1);
585
586 /* We only support single-subsection segments */
587 ASSERT(Subsection->SubsectionBase != NULL);
588 ASSERT(Vad->FirstPrototypePte >= Subsection->SubsectionBase);
589 ASSERT(Vad->FirstPrototypePte < &Subsection->SubsectionBase[Subsection->PtesInSubsection]);
590
591 /* Compute the PTE offset */
592 PteOffset = Vpn - Vad->StartingVpn;
593 PteOffset += Vad->FirstPrototypePte - Subsection->SubsectionBase;
594
595 /* Again, we only support single-subsection segments */
596 ASSERT(PteOffset < 0xF0000000);
597 ASSERT(PteOffset < Subsection->PtesInSubsection);
598
599 /* Return the subsection */
600 return Subsection;
601 }
602
603 VOID
604 NTAPI
605 MiSegmentDelete(IN PSEGMENT Segment)
606 {
607 PCONTROL_AREA ControlArea;
608 SEGMENT_FLAGS SegmentFlags;
609 PSUBSECTION Subsection;
610 PMMPTE PointerPte, LastPte, PteForProto;
611 PMMPFN Pfn1;
612 PFN_NUMBER PageFrameIndex;
613 MMPTE TempPte;
614 KIRQL OldIrql;
615
616 /* Capture data */
617 SegmentFlags = Segment->SegmentFlags;
618 ControlArea = Segment->ControlArea;
619
620 /* Make sure control area is on the right delete path */
621 ASSERT(ControlArea->u.Flags.BeingDeleted == 1);
622 ASSERT(ControlArea->WritableUserReferences == 0);
623
624 /* These things are not supported yet */
625 ASSERT(ControlArea->DereferenceList.Flink == NULL);
626 ASSERT(!(ControlArea->u.Flags.Image) && !(ControlArea->u.Flags.File));
627 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
628 ASSERT(ControlArea->u.Flags.Rom == 0);
629
630 /* Get the subsection and PTEs for this segment */
631 Subsection = (PSUBSECTION)(ControlArea + 1);
632 PointerPte = Subsection->SubsectionBase;
633 LastPte = PointerPte + Segment->NonExtendedPtes;
634
635 /* Lock the PFN database */
636 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
637
638 /* Check if the master PTE is invalid */
639 PteForProto = MiAddressToPte(PointerPte);
640 if (!PteForProto->u.Hard.Valid)
641 {
642 /* Fault it in */
643 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
644 }
645
646 /* Loop all the segment PTEs */
647 while (PointerPte < LastPte)
648 {
649 /* Check if it's time to switch master PTEs if we passed a PDE boundary */
650 if (!((ULONG_PTR)PointerPte & (PD_SIZE - 1)) &&
651 (PointerPte != Subsection->SubsectionBase))
652 {
653 /* Check if the master PTE is invalid */
654 PteForProto = MiAddressToPte(PointerPte);
655 if (!PteForProto->u.Hard.Valid)
656 {
657 /* Fault it in */
658 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
659 }
660 }
661
662 /* This should be a prototype PTE */
663 TempPte = *PointerPte;
664 ASSERT(SegmentFlags.LargePages == 0);
665 ASSERT(TempPte.u.Hard.Valid == 0);
666
667 /* See if we should clean things up */
668 if (!(ControlArea->u.Flags.Image) && !(ControlArea->u.Flags.File))
669 {
670 /*
671 * This is a section backed by the pagefile. Now that it doesn't exist anymore,
672 * we can give everything back to the system.
673 */
674 ASSERT(TempPte.u.Soft.Prototype == 0);
675
676 if (TempPte.u.Soft.Transition == 1)
677 {
678 /* We can give the page back for other use */
679 DPRINT("Releasing page for transition PTE %p\n", PointerPte);
680 PageFrameIndex = PFN_FROM_PTE(&TempPte);
681 Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
682
683 /* As this is a paged-backed section, nobody should reference it anymore (no cache or whatever) */
684 ASSERT(Pfn1->u3.ReferenceCount == 0);
685
686 /* And it should be in standby or modified list */
687 ASSERT((Pfn1->u3.e1.PageLocation == ModifiedPageList) || (Pfn1->u3.e1.PageLocation == StandbyPageList));
688
689 /* Unlink it and put it back in free list */
690 MiUnlinkPageFromList(Pfn1);
691
692 /* Temporarily mark this as active and make it free again */
693 Pfn1->u3.e1.PageLocation = ActiveAndValid;
694 MI_SET_PFN_DELETED(Pfn1);
695
696 MiInsertPageInFreeList(PageFrameIndex);
697 }
698 else if (TempPte.u.Soft.PageFileHigh != 0)
699 {
700 /* Should not happen for now */
701 ASSERT(FALSE);
702 }
703 }
704 else
705 {
706 /* unsupported for now */
707 ASSERT(FALSE);
708
709 /* File-backed section must have prototype PTEs */
710 ASSERT(TempPte.u.Soft.Prototype == 1);
711 }
712
713 /* Zero the PTE and keep going */
714 PointerPte->u.Long = 0;
715 PointerPte++;
716 }
717
718 /* Release the PFN lock */
719 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
720
721 /* Free the structures */
722 ExFreePool(ControlArea);
723 ExFreePool(Segment);
724 }
725
726 VOID
727 NTAPI
728 MiCheckControlArea(IN PCONTROL_AREA ControlArea,
729 IN KIRQL OldIrql)
730 {
731 BOOLEAN DeleteSegment = FALSE;
732 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
733
734 /* Check if this is the last reference or view */
735 if (!(ControlArea->NumberOfMappedViews) &&
736 !(ControlArea->NumberOfSectionReferences))
737 {
738 /* There should be no more user references either */
739 ASSERT(ControlArea->NumberOfUserReferences == 0);
740
741 /* Not yet supported */
742 ASSERT(ControlArea->FilePointer == NULL);
743
744 /* The control area is being destroyed */
745 ControlArea->u.Flags.BeingDeleted = TRUE;
746 DeleteSegment = TRUE;
747 }
748
749 /* Release the PFN lock */
750 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
751
752 /* Delete the segment if needed */
753 if (DeleteSegment)
754 {
755 /* No more user write references at all */
756 ASSERT(ControlArea->WritableUserReferences == 0);
757 MiSegmentDelete(ControlArea->Segment);
758 }
759 }
760
761 VOID
762 NTAPI
763 MiDereferenceControlArea(IN PCONTROL_AREA ControlArea)
764 {
765 KIRQL OldIrql;
766
767 /* Lock the PFN database */
768 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
769
770 /* Drop reference counts */
771 ControlArea->NumberOfMappedViews--;
772 ControlArea->NumberOfUserReferences--;
773
774 /* Check if it's time to delete the CA. This releases the lock */
775 MiCheckControlArea(ControlArea, OldIrql);
776 }
777
778 VOID
779 NTAPI
780 MiRemoveMappedView(IN PEPROCESS CurrentProcess,
781 IN PMMVAD Vad)
782 {
783 KIRQL OldIrql;
784 PCONTROL_AREA ControlArea;
785 PETHREAD CurrentThread = PsGetCurrentThread();
786
787 /* Get the control area */
788 ControlArea = Vad->ControlArea;
789
790 /* We only support non-extendable, non-image, pagefile-backed regular sections */
791 ASSERT(Vad->u.VadFlags.VadType == VadNone);
792 ASSERT(Vad->u2.VadFlags2.ExtendableFile == FALSE);
793 ASSERT(ControlArea);
794 ASSERT(ControlArea->FilePointer == NULL);
795
796 /* Delete the actual virtual memory pages */
797 MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
798 (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
799 Vad);
800
801 /* Release the working set */
802 MiUnlockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread);
803
804 /* Lock the PFN database */
805 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
806
807 /* Remove references */
808 ControlArea->NumberOfMappedViews--;
809 ControlArea->NumberOfUserReferences--;
810
811 /* Check if it should be destroyed */
812 MiCheckControlArea(ControlArea, OldIrql);
813 }
814
815 NTSTATUS
816 NTAPI
817 MiUnmapViewOfSection(IN PEPROCESS Process,
818 IN PVOID BaseAddress,
819 IN ULONG Flags)
820 {
821 PMEMORY_AREA MemoryArea;
822 BOOLEAN Attached = FALSE;
823 KAPC_STATE ApcState;
824 PMMVAD Vad;
825 PVOID DbgBase = NULL;
826 SIZE_T RegionSize;
827 NTSTATUS Status;
828 PETHREAD CurrentThread = PsGetCurrentThread();
829 PEPROCESS CurrentProcess = PsGetCurrentProcess();
830 PAGED_CODE();
831
832 /* Check for Mm Region */
833 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, BaseAddress);
834 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
835 {
836 /* Call Mm API */
837 return MiRosUnmapViewOfSection(Process, BaseAddress, Flags);
838 }
839
840 /* Check if we should attach to the process */
841 if (CurrentProcess != Process)
842 {
843 /* The process is different, do an attach */
844 KeStackAttachProcess(&Process->Pcb, &ApcState);
845 Attached = TRUE;
846 }
847
848 /* Check if we need to lock the address space */
849 if (!Flags) MmLockAddressSpace(&Process->Vm);
850
851 /* Check if the process is already daed */
852 if (Process->VmDeleted)
853 {
854 /* Fail the call */
855 DPRINT1("Process died!\n");
856 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
857 Status = STATUS_PROCESS_IS_TERMINATING;
858 goto Quickie;
859 }
860
861 /* Find the VAD for the address and make sure it's a section VAD */
862 Vad = MiLocateAddress(BaseAddress);
863 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory))
864 {
865 /* Couldn't find it, or invalid VAD, fail */
866 DPRINT1("No VAD or invalid VAD\n");
867 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
868 Status = STATUS_NOT_MAPPED_VIEW;
869 goto Quickie;
870 }
871
872 /* We should be attached */
873 ASSERT(Process == PsGetCurrentProcess());
874
875 /* We need the base address for the debugger message on image-backed VADs */
876 if (Vad->u.VadFlags.VadType == VadImageMap)
877 {
878 DbgBase = (PVOID)(Vad->StartingVpn >> PAGE_SHIFT);
879 }
880
881 /* Compute the size of the VAD region */
882 RegionSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
883
884 /* For SEC_NO_CHANGE sections, we need some extra checks */
885 if (Vad->u.VadFlags.NoChange == 1)
886 {
887 /* Are we allowed to mess with this VAD? */
888 Status = MiCheckSecuredVad(Vad,
889 (PVOID)(Vad->StartingVpn >> PAGE_SHIFT),
890 RegionSize,
891 MM_DELETE_CHECK);
892 if (!NT_SUCCESS(Status))
893 {
894 /* We failed */
895 DPRINT1("Trying to unmap protected VAD!\n");
896 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
897 goto Quickie;
898 }
899 }
900
901 /* Not currently supported */
902 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
903
904 /* FIXME: Remove VAD charges */
905
906 /* Lock the working set */
907 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
908
909 /* Remove the VAD */
910 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
911 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
912
913 /* Remove the PTEs for this view, which also releases the working set lock */
914 MiRemoveMappedView(Process, Vad);
915
916 /* FIXME: Remove commitment */
917
918 /* Update performance counter and release the lock */
919 Process->VirtualSize -= RegionSize;
920 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
921
922 /* Destroy the VAD and return success */
923 ExFreePool(Vad);
924 Status = STATUS_SUCCESS;
925
926 /* Failure and success case -- send debugger message, detach, and return */
927 Quickie:
928 if (DbgBase) DbgkUnMapViewOfSection(DbgBase);
929 if (Attached) KeUnstackDetachProcess(&ApcState);
930 return Status;
931 }
932
933 NTSTATUS
934 NTAPI
935 MiSessionCommitPageTables(IN PVOID StartVa,
936 IN PVOID EndVa)
937 {
938 KIRQL OldIrql;
939 ULONG Color, Index;
940 PMMPDE StartPde, EndPde;
941 MMPDE TempPde = ValidKernelPdeLocal;
942 PMMPFN Pfn1;
943 PFN_NUMBER PageCount = 0, ActualPages = 0, PageFrameNumber;
944
945 /* Windows sanity checks */
946 ASSERT(StartVa >= (PVOID)MmSessionBase);
947 ASSERT(EndVa < (PVOID)MiSessionSpaceEnd);
948 ASSERT(PAGE_ALIGN(EndVa) == EndVa);
949
950 /* Get the start and end PDE, then loop each one */
951 StartPde = MiAddressToPde(StartVa);
952 EndPde = MiAddressToPde((PVOID)((ULONG_PTR)EndVa - 1));
953 Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22;
954 while (StartPde <= EndPde)
955 {
956 #ifndef _M_AMD64
957 /* If we don't already have a page table for it, increment count */
958 if (MmSessionSpace->PageTables[Index].u.Long == 0) PageCount++;
959 #endif
960 /* Move to the next one */
961 StartPde++;
962 Index++;
963 }
964
965 /* If there's no page tables to create, bail out */
966 if (PageCount == 0) return STATUS_SUCCESS;
967
968 /* Reset the start PDE and index */
969 StartPde = MiAddressToPde(StartVa);
970 Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22;
971
972 /* Loop each PDE while holding the working set lock */
973 // MiLockWorkingSet(PsGetCurrentThread(),
974 // &MmSessionSpace->GlobalVirtualAddress->Vm);
975 #ifdef _M_AMD64
976 _WARN("MiSessionCommitPageTables halfplemented for amd64")
977 DBG_UNREFERENCED_LOCAL_VARIABLE(OldIrql);
978 DBG_UNREFERENCED_LOCAL_VARIABLE(Color);
979 DBG_UNREFERENCED_LOCAL_VARIABLE(TempPde);
980 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn1);
981 DBG_UNREFERENCED_LOCAL_VARIABLE(PageFrameNumber);
982 ASSERT(FALSE);
983 #else
984 while (StartPde <= EndPde)
985 {
986 /* Check if we already have a page table */
987 if (MmSessionSpace->PageTables[Index].u.Long == 0)
988 {
989 /* We don't, so the PDE shouldn't be ready yet */
990 ASSERT(StartPde->u.Hard.Valid == 0);
991
992 /* ReactOS check to avoid MiEnsureAvailablePageOrWait */
993 ASSERT(MmAvailablePages >= 32);
994
995 /* Acquire the PFN lock and grab a zero page */
996 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
997 Color = (++MmSessionSpace->Color) & MmSecondaryColorMask;
998 PageFrameNumber = MiRemoveZeroPage(Color);
999 TempPde.u.Hard.PageFrameNumber = PageFrameNumber;
1000 MI_WRITE_VALID_PDE(StartPde, TempPde);
1001
1002 /* Write the page table in session space structure */
1003 ASSERT(MmSessionSpace->PageTables[Index].u.Long == 0);
1004 MmSessionSpace->PageTables[Index] = TempPde;
1005
1006 /* Initialize the PFN */
1007 MiInitializePfnForOtherProcess(PageFrameNumber,
1008 StartPde,
1009 MmSessionSpace->SessionPageDirectoryIndex);
1010
1011 /* And now release the lock */
1012 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1013
1014 /* Get the PFN entry and make sure there's no event for it */
1015 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
1016 ASSERT(Pfn1->u1.Event == NULL);
1017
1018 /* Increment the number of pages */
1019 ActualPages++;
1020 }
1021
1022 /* Move to the next PDE */
1023 StartPde++;
1024 Index++;
1025 }
1026 #endif
1027
1028 /* Make sure we didn't do more pages than expected */
1029 ASSERT(ActualPages <= PageCount);
1030
1031 /* Release the working set lock */
1032 // MiUnlockWorkingSet(PsGetCurrentThread(),
1033 // &MmSessionSpace->GlobalVirtualAddress->Vm);
1034
1035
1036 /* If we did at least one page... */
1037 if (ActualPages)
1038 {
1039 /* Update the performance counters! */
1040 InterlockedExchangeAddSizeT(&MmSessionSpace->NonPageablePages, ActualPages);
1041 InterlockedExchangeAddSizeT(&MmSessionSpace->CommittedPages, ActualPages);
1042 }
1043
1044 /* Return status */
1045 return STATUS_SUCCESS;
1046 }
1047
1048 NTSTATUS
1049 NTAPI
1050 MiMapViewInSystemSpace(IN PVOID Section,
1051 IN PMMSESSION Session,
1052 OUT PVOID *MappedBase,
1053 IN OUT PSIZE_T ViewSize)
1054 {
1055 PVOID Base;
1056 PCONTROL_AREA ControlArea;
1057 ULONG Buckets, SectionSize;
1058 NTSTATUS Status;
1059 PAGED_CODE();
1060
1061 /* Get the control area, check for any flags ARM3 doesn't yet support */
1062 ControlArea = ((PSECTION)Section)->Segment->ControlArea;
1063 ASSERT(ControlArea->u.Flags.Image == 0);
1064 ASSERT(ControlArea->FilePointer == NULL);
1065 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1066 ASSERT(ControlArea->u.Flags.Rom == 0);
1067 ASSERT(ControlArea->u.Flags.WasPurged == 0);
1068
1069 /* Increase the reference and map count on the control area, no purges yet */
1070 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1071 ASSERT(NT_SUCCESS(Status));
1072
1073 /* Get the section size at creation time */
1074 SectionSize = ((PSECTION)Section)->SizeOfSection.LowPart;
1075
1076 /* If the caller didn't specify a view size, assume the whole section */
1077 if (!(*ViewSize)) *ViewSize = SectionSize;
1078
1079 /* Check if the caller wanted a larger section than the view */
1080 if (*ViewSize > SectionSize)
1081 {
1082 /* Fail */
1083 DPRINT1("View is too large\n");
1084 MiDereferenceControlArea(ControlArea);
1085 return STATUS_INVALID_VIEW_SIZE;
1086 }
1087
1088 /* Get the number of 64K buckets required for this mapping */
1089 Buckets = (ULONG)(*ViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE);
1090 if (*ViewSize & (MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) Buckets++;
1091
1092 /* Check if the view is more than 4GB large */
1093 if (Buckets >= MI_SYSTEM_VIEW_BUCKET_SIZE)
1094 {
1095 /* Fail */
1096 DPRINT1("View is too large\n");
1097 MiDereferenceControlArea(ControlArea);
1098 return STATUS_INVALID_VIEW_SIZE;
1099 }
1100
1101 /* Insert this view into system space and get a base address for it */
1102 Base = MiInsertInSystemSpace(Session, Buckets, ControlArea);
1103 if (!Base)
1104 {
1105 /* Fail */
1106 DPRINT1("Out of system space\n");
1107 MiDereferenceControlArea(ControlArea);
1108 return STATUS_NO_MEMORY;
1109 }
1110
1111 /* What's the underlying session? */
1112 if (Session == &MmSession)
1113 {
1114 /* Create the PDEs needed for this mapping, and double-map them if needed */
1115 MiFillSystemPageDirectory(Base, Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE);
1116 Status = STATUS_SUCCESS;
1117 }
1118 else
1119 {
1120 /* Create the PDEs needed for this mapping */
1121 Status = MiSessionCommitPageTables(Base,
1122 (PVOID)((ULONG_PTR)Base +
1123 Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE));
1124 ASSERT(NT_SUCCESS(Status));
1125 }
1126
1127 /* Create the actual prototype PTEs for this mapping */
1128 Status = MiAddMappedPtes(MiAddressToPte(Base),
1129 BYTES_TO_PAGES(*ViewSize),
1130 ControlArea);
1131 ASSERT(NT_SUCCESS(Status));
1132
1133 /* Return the base adress of the mapping and success */
1134 *MappedBase = Base;
1135 return STATUS_SUCCESS;
1136 }
1137
1138 VOID
1139 NTAPI
1140 MiSetControlAreaSymbolsLoaded(IN PCONTROL_AREA ControlArea)
1141 {
1142 KIRQL OldIrql;
1143
1144 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1145
1146 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1147 ControlArea->u.Flags.DebugSymbolsLoaded |= 1;
1148
1149 ASSERT(OldIrql <= APC_LEVEL);
1150 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1151 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1152 }
1153
1154 VOID
1155 NTAPI
1156 MiLoadUserSymbols(IN PCONTROL_AREA ControlArea,
1157 IN PVOID BaseAddress,
1158 IN PEPROCESS Process)
1159 {
1160 NTSTATUS Status;
1161 ANSI_STRING FileNameA;
1162 PLIST_ENTRY NextEntry;
1163 PUNICODE_STRING FileName;
1164 PIMAGE_NT_HEADERS NtHeaders;
1165 PLDR_DATA_TABLE_ENTRY LdrEntry;
1166
1167 FileName = &ControlArea->FilePointer->FileName;
1168 if (FileName->Length == 0)
1169 {
1170 return;
1171 }
1172
1173 /* Acquire module list lock */
1174 KeEnterCriticalRegion();
1175 ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
1176
1177 /* Browse list to try to find current module */
1178 for (NextEntry = MmLoadedUserImageList.Flink;
1179 NextEntry != &MmLoadedUserImageList;
1180 NextEntry = NextEntry->Flink)
1181 {
1182 /* Get the entry */
1183 LdrEntry = CONTAINING_RECORD(NextEntry,
1184 LDR_DATA_TABLE_ENTRY,
1185 InLoadOrderLinks);
1186
1187 /* If already in the list, increase load count */
1188 if (LdrEntry->DllBase == BaseAddress)
1189 {
1190 ++LdrEntry->LoadCount;
1191 break;
1192 }
1193 }
1194
1195 /* Not in the list, we'll add it */
1196 if (NextEntry == &MmLoadedUserImageList)
1197 {
1198 /* Allocate our element, taking to the name string and its null char */
1199 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry), 'bDmM');
1200 if (LdrEntry)
1201 {
1202 memset(LdrEntry, 0, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry));
1203
1204 _SEH2_TRY
1205 {
1206 /* Get image checksum and size */
1207 NtHeaders = RtlImageNtHeader(BaseAddress);
1208 if (NtHeaders)
1209 {
1210 LdrEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
1211 LdrEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
1212 }
1213 }
1214 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1215 {
1216 ExFreePoolWithTag(LdrEntry, 'bDmM');
1217 _SEH2_YIELD(return);
1218 }
1219 _SEH2_END;
1220
1221 /* Fill all the details */
1222 LdrEntry->DllBase = BaseAddress;
1223 LdrEntry->FullDllName.Buffer = (PVOID)((ULONG_PTR)LdrEntry + sizeof(*LdrEntry));
1224 LdrEntry->FullDllName.Length = FileName->Length;
1225 LdrEntry->FullDllName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL);
1226 memcpy(LdrEntry->FullDllName.Buffer, FileName->Buffer, FileName->Length);
1227 LdrEntry->FullDllName.Buffer[LdrEntry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1228 LdrEntry->LoadCount = 1;
1229
1230 /* Insert! */
1231 InsertHeadList(&MmLoadedUserImageList, &LdrEntry->InLoadOrderLinks);
1232 }
1233 }
1234
1235 /* Release locks */
1236 ExReleaseResourceLite(&PsLoadedModuleResource);
1237 KeLeaveCriticalRegion();
1238
1239 /* Load symbols */
1240 Status = RtlUnicodeStringToAnsiString(&FileNameA, FileName, TRUE);
1241 if (NT_SUCCESS(Status))
1242 {
1243 DbgLoadImageSymbols(&FileNameA, BaseAddress, (ULONG_PTR)Process->UniqueProcessId);
1244 RtlFreeAnsiString(&FileNameA);
1245 }
1246 }
1247
1248 NTSTATUS
1249 NTAPI
1250 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
1251 IN PEPROCESS Process,
1252 IN PVOID *BaseAddress,
1253 IN PLARGE_INTEGER SectionOffset,
1254 IN PSIZE_T ViewSize,
1255 IN PSECTION Section,
1256 IN SECTION_INHERIT InheritDisposition,
1257 IN ULONG ProtectionMask,
1258 IN SIZE_T CommitSize,
1259 IN ULONG_PTR ZeroBits,
1260 IN ULONG AllocationType)
1261 {
1262 PMMVAD_LONG Vad;
1263 ULONG_PTR StartAddress;
1264 ULONG_PTR ViewSizeInPages;
1265 PSUBSECTION Subsection;
1266 PSEGMENT Segment;
1267 PFN_NUMBER PteOffset;
1268 NTSTATUS Status;
1269 ULONG QuotaCharge = 0, QuotaExcess = 0;
1270 PMMPTE PointerPte, LastPte;
1271 MMPTE TempPte;
1272 ULONG Granularity = MM_VIRTMEM_GRANULARITY;
1273
1274 DPRINT("Mapping ARM3 data section\n");
1275
1276 /* Get the segment for this section */
1277 Segment = ControlArea->Segment;
1278
1279 #ifdef _M_IX86
1280 /* ALlow being less restrictive on x86. */
1281 if (AllocationType & MEM_DOS_LIM)
1282 Granularity = PAGE_SIZE;
1283 #endif
1284
1285 /* One can only reserve a file-based mapping, not shared memory! */
1286 if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer))
1287 {
1288 return STATUS_INVALID_PARAMETER_9;
1289 }
1290
1291 /* First, increase the map count. No purging is supported yet */
1292 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1293 if (!NT_SUCCESS(Status)) return Status;
1294
1295 /* Check if the caller specified the view size */
1296 if (!(*ViewSize))
1297 {
1298 /* The caller did not, so pick a 64K aligned view size based on the offset */
1299 SectionOffset->LowPart &= ~(_64K - 1);
1300 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
1301 }
1302 else
1303 {
1304 /* A size was specified, align it to a 64K boundary */
1305 *ViewSize += SectionOffset->LowPart & (_64K - 1);
1306
1307 /* Align the offset as well to make this an aligned map */
1308 SectionOffset->LowPart &= ~((ULONG)_64K - 1);
1309 }
1310
1311 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */
1312 ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0);
1313
1314 /* It's illegal to try to map more than overflows a LONG_PTR */
1315 if (*ViewSize >= MAXLONG_PTR)
1316 {
1317 MiDereferenceControlArea(ControlArea);
1318 return STATUS_INVALID_VIEW_SIZE;
1319 }
1320
1321 /* Windows ASSERTs for this flag */
1322 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1323
1324 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
1325 ASSERT(ControlArea->u.Flags.Rom == 0);
1326 Subsection = (PSUBSECTION)(ControlArea + 1);
1327
1328 /* Sections with extended segments are not supported in ARM3 */
1329 ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0);
1330
1331 /* Within this section, figure out which PTEs will describe the view */
1332 PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT);
1333
1334 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */
1335 ASSERT(PteOffset < Segment->TotalNumberOfPtes);
1336 ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset);
1337
1338 /* In ARM3, only one subsection is used for now. It must contain these PTEs */
1339 ASSERT(PteOffset < Subsection->PtesInSubsection);
1340
1341 /* In ARM3, only page-file backed sections (shared memory) are supported now */
1342 ASSERT(ControlArea->FilePointer == NULL);
1343
1344 /* Windows ASSERTs for this too -- there must be a subsection base address */
1345 ASSERT(Subsection->SubsectionBase != NULL);
1346
1347 /* Compute how much commit space the segment will take */
1348 if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes))
1349 {
1350 /* Charge for the maximum pages */
1351 QuotaCharge = BYTES_TO_PAGES(CommitSize);
1352 }
1353
1354 /* ARM3 does not currently support large pages */
1355 ASSERT(Segment->SegmentFlags.LargePages == 0);
1356
1357 /* Calculate how many pages the region spans */
1358 ViewSizeInPages = BYTES_TO_PAGES(*ViewSize);
1359
1360 /* A VAD can now be allocated. Do so and zero it out */
1361 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */
1362 ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */
1363 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
1364 if (!Vad)
1365 {
1366 MiDereferenceControlArea(ControlArea);
1367 return STATUS_INSUFFICIENT_RESOURCES;
1368 }
1369
1370 RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
1371 Vad->u4.Banked = (PVOID)0xDEADBABE;
1372
1373 /* Write all the data required in the VAD for handling a fault */
1374 Vad->ControlArea = ControlArea;
1375 Vad->u.VadFlags.CommitCharge = 0;
1376 Vad->u.VadFlags.Protection = ProtectionMask;
1377 Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16);
1378 Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
1379 if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange))
1380 {
1381 /* This isn't really implemented yet, but handle setting the flag */
1382 Vad->u.VadFlags.NoChange = 1;
1383 Vad->u2.VadFlags2.SecNoChange = 1;
1384 }
1385
1386 /* Finally, write down the first and last prototype PTE */
1387 Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
1388 PteOffset += ViewSizeInPages - 1;
1389 ASSERT(PteOffset < Subsection->PtesInSubsection);
1390 Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset];
1391
1392 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */
1393 ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
1394
1395 /* FIXME: Should setup VAD bitmap */
1396 Status = STATUS_SUCCESS;
1397
1398 /* Check if anything was committed */
1399 if (QuotaCharge)
1400 {
1401 /* Set the start and end PTE addresses, and pick the template PTE */
1402 PointerPte = Vad->FirstPrototypePte;
1403 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
1404 TempPte = Segment->SegmentPteTemplate;
1405
1406 /* Acquire the commit lock and loop all prototype PTEs to be committed */
1407 KeAcquireGuardedMutex(&MmSectionCommitMutex);
1408 while (PointerPte < LastPte)
1409 {
1410 /* Make sure the PTE is already invalid */
1411 if (PointerPte->u.Long == 0)
1412 {
1413 /* And write the invalid PTE */
1414 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1415 }
1416 else
1417 {
1418 /* The PTE is valid, so skip it */
1419 QuotaExcess++;
1420 }
1421
1422 /* Move to the next PTE */
1423 PointerPte++;
1424 }
1425
1426 /* Now check how many pages exactly we committed, and update accounting */
1427 ASSERT(QuotaCharge >= QuotaExcess);
1428 QuotaCharge -= QuotaExcess;
1429 Segment->NumberOfCommittedPages += QuotaCharge;
1430 ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes);
1431
1432 /* Now that we're done, release the lock */
1433 KeReleaseGuardedMutex(&MmSectionCommitMutex);
1434 }
1435
1436 /* Is it SEC_BASED, or did the caller manually specify an address? */
1437 if (*BaseAddress != NULL)
1438 {
1439 /* Just align what the caller gave us */
1440 StartAddress = ALIGN_DOWN_BY((ULONG_PTR)*BaseAddress, Granularity);
1441 }
1442 else if (Section->Address.StartingVpn != 0)
1443 {
1444 /* It is a SEC_BASED mapping, use the address that was generated */
1445 StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart;
1446 }
1447 else
1448 {
1449 StartAddress = 0;
1450 }
1451
1452 /* Insert the VAD */
1453 Status = MiInsertVadEx((PMMVAD)Vad,
1454 &StartAddress,
1455 ViewSizeInPages * PAGE_SIZE,
1456 MAXULONG_PTR >> ZeroBits,
1457 Granularity,
1458 AllocationType);
1459 if (!NT_SUCCESS(Status))
1460 {
1461 return Status;
1462 }
1463
1464 /* Windows stores this for accounting purposes, do so as well */
1465 if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress;
1466
1467 /* Finally, let the caller know where, and for what size, the view was mapped */
1468 *ViewSize = ViewSizeInPages * PAGE_SIZE;
1469 *BaseAddress = (PVOID)StartAddress;
1470 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize);
1471 return STATUS_SUCCESS;
1472 }
1473
1474 VOID
1475 NTAPI
1476 MiSubsectionConsistent(IN PSUBSECTION Subsection)
1477 {
1478 /* ReactOS only supports systems with 4K pages and 4K sectors */
1479 ASSERT(Subsection->u.SubsectionFlags.SectorEndOffset == 0);
1480
1481 /* Therefore, then number of PTEs should be equal to the number of sectors */
1482 if (Subsection->NumberOfFullSectors != Subsection->PtesInSubsection)
1483 {
1484 /* Break and warn if this is inconsistent */
1485 DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n",
1486 Subsection->NumberOfFullSectors, Subsection->PtesInSubsection);
1487 DbgBreakPoint();
1488 }
1489 }
1490
1491 NTSTATUS
1492 NTAPI
1493 MiCreateDataFileMap(IN PFILE_OBJECT File,
1494 OUT PSEGMENT *Segment,
1495 IN PSIZE_T MaximumSize,
1496 IN ULONG SectionPageProtection,
1497 IN ULONG AllocationAttributes,
1498 IN ULONG IgnoreFileSizing)
1499 {
1500 /* Not yet implemented */
1501 ASSERT(FALSE);
1502 *Segment = NULL;
1503 return STATUS_NOT_IMPLEMENTED;
1504 }
1505
1506 NTSTATUS
1507 NTAPI
1508 MiCreatePagingFileMap(OUT PSEGMENT *Segment,
1509 IN PSIZE_T MaximumSize,
1510 IN ULONG ProtectionMask,
1511 IN ULONG AllocationAttributes)
1512 {
1513 SIZE_T SizeLimit;
1514 PFN_COUNT PteCount;
1515 PMMPTE PointerPte;
1516 MMPTE TempPte;
1517 PCONTROL_AREA ControlArea;
1518 PSEGMENT NewSegment;
1519 PSUBSECTION Subsection;
1520 PAGED_CODE();
1521
1522 /* No large pages in ARM3 yet */
1523 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
1524
1525 /* Pagefile-backed sections need a known size */
1526 if (!(*MaximumSize)) return STATUS_INVALID_PARAMETER_4;
1527
1528 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */
1529 SizeLimit = MAXULONG_PTR - sizeof(SEGMENT);
1530 SizeLimit /= sizeof(MMPTE);
1531 SizeLimit <<= PAGE_SHIFT;
1532
1533 /* Fail if this size is too big */
1534 if (*MaximumSize > SizeLimit) return STATUS_SECTION_TOO_BIG;
1535
1536 /* Calculate how many Prototype PTEs will be needed */
1537 PteCount = (PFN_COUNT)((*MaximumSize + PAGE_SIZE - 1) >> PAGE_SHIFT);
1538
1539 /* For commited memory, we must have a valid protection mask */
1540 if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0);
1541
1542 /* The segment contains all the Prototype PTEs, allocate it in paged pool */
1543 NewSegment = ExAllocatePoolWithTag(PagedPool,
1544 sizeof(SEGMENT) +
1545 sizeof(MMPTE) * (PteCount - 1),
1546 'tSmM');
1547 ASSERT(NewSegment);
1548 *Segment = NewSegment;
1549
1550 /* Now allocate the control area, which has the subsection structure */
1551 ControlArea = ExAllocatePoolWithTag(NonPagedPool,
1552 sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
1553 'tCmM');
1554 ASSERT(ControlArea);
1555
1556 /* And zero it out, filling the basic segmnet pointer and reference fields */
1557 RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
1558 ControlArea->Segment = NewSegment;
1559 ControlArea->NumberOfSectionReferences = 1;
1560 ControlArea->NumberOfUserReferences = 1;
1561
1562 /* Convert allocation attributes to control area flags */
1563 if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1;
1564 if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1;
1565 if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1;
1566
1567 /* We just allocated it */
1568 ControlArea->u.Flags.BeingCreated = 1;
1569
1570 /* The subsection follows, write the mask, PTE count and point back to the CA */
1571 Subsection = (PSUBSECTION)(ControlArea + 1);
1572 Subsection->ControlArea = ControlArea;
1573 Subsection->PtesInSubsection = PteCount;
1574 Subsection->u.SubsectionFlags.Protection = ProtectionMask;
1575
1576 /* Zero out the segment's prototype PTEs, and link it with the control area */
1577 PointerPte = &NewSegment->ThePtes[0];
1578 RtlZeroMemory(NewSegment, sizeof(SEGMENT));
1579 NewSegment->PrototypePte = PointerPte;
1580 NewSegment->ControlArea = ControlArea;
1581
1582 /* Save some extra accounting data for the segment as well */
1583 NewSegment->u1.CreatingProcess = PsGetCurrentProcess();
1584 NewSegment->SizeOfSegment = PteCount * PAGE_SIZE;
1585 NewSegment->TotalNumberOfPtes = PteCount;
1586 NewSegment->NonExtendedPtes = PteCount;
1587
1588 /* The subsection's base address is the first Prototype PTE in the segment */
1589 Subsection->SubsectionBase = PointerPte;
1590
1591 /* Start with an empty PTE, unless this is a commit operation */
1592 TempPte.u.Long = 0;
1593 if (AllocationAttributes & SEC_COMMIT)
1594 {
1595 /* In which case, write down the protection mask in the Prototype PTEs */
1596 TempPte.u.Soft.Protection = ProtectionMask;
1597
1598 /* For accounting, also mark these pages as being committed */
1599 NewSegment->NumberOfCommittedPages = PteCount;
1600 }
1601
1602 /* The template PTE itself for the segment should also have the mask set */
1603 NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
1604
1605 /* Write out the prototype PTEs, for now they're simply demand zero */
1606 #ifdef _WIN64
1607 RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1608 #else
1609 RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1610 #endif
1611 return STATUS_SUCCESS;
1612 }
1613
1614 NTSTATUS
1615 NTAPI
1616 MiGetFileObjectForSectionAddress(
1617 IN PVOID Address,
1618 OUT PFILE_OBJECT *FileObject)
1619 {
1620 PMMVAD Vad;
1621 PCONTROL_AREA ControlArea;
1622
1623 /* Get the VAD */
1624 Vad = MiLocateAddress(Address);
1625 if (Vad == NULL)
1626 {
1627 /* Fail, the address does not exist */
1628 DPRINT1("Invalid address\n");
1629 return STATUS_INVALID_ADDRESS;
1630 }
1631
1632 /* Check if this is a RosMm memory area */
1633 if (Vad->u.VadFlags.Spare != 0)
1634 {
1635 PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad;
1636 PROS_SECTION_OBJECT Section;
1637
1638 /* Check if it's a section view (RosMm section) */
1639 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1640 {
1641 /* Get the section pointer to the SECTION_OBJECT */
1642 Section = MemoryArea->Data.SectionData.Section;
1643 *FileObject = Section->FileObject;
1644 }
1645 else
1646 {
1647 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE);
1648 DPRINT1("Address is a cache section!\n");
1649 return STATUS_SECTION_NOT_IMAGE;
1650 }
1651 }
1652 else
1653 {
1654 /* Make sure it's not a VM VAD */
1655 if (Vad->u.VadFlags.PrivateMemory == 1)
1656 {
1657 DPRINT1("Address is not a section\n");
1658 return STATUS_SECTION_NOT_IMAGE;
1659 }
1660
1661 /* Get the control area */
1662 ControlArea = Vad->ControlArea;
1663 if (!(ControlArea) || !(ControlArea->u.Flags.Image))
1664 {
1665 DPRINT1("Address is not a section\n");
1666 return STATUS_SECTION_NOT_IMAGE;
1667 }
1668
1669 /* Get the file object */
1670 *FileObject = ControlArea->FilePointer;
1671 }
1672
1673 /* Return success */
1674 return STATUS_SUCCESS;
1675 }
1676
1677 PFILE_OBJECT
1678 NTAPI
1679 MmGetFileObjectForSection(IN PVOID SectionObject)
1680 {
1681 PSECTION_OBJECT Section;
1682 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1683 ASSERT(SectionObject != NULL);
1684
1685 /* Check if it's an ARM3, or ReactOS section */
1686 if (MiIsRosSectionObject(SectionObject) == FALSE)
1687 {
1688 /* Return the file pointer stored in the control area */
1689 Section = SectionObject;
1690 return Section->Segment->ControlArea->FilePointer;
1691 }
1692
1693 /* Return the file object */
1694 return ((PROS_SECTION_OBJECT)SectionObject)->FileObject;
1695 }
1696
1697 static
1698 PFILE_OBJECT
1699 MiGetFileObjectForVad(
1700 _In_ PMMVAD Vad)
1701 {
1702 PCONTROL_AREA ControlArea;
1703 PFILE_OBJECT FileObject;
1704
1705 /* Check if this is a RosMm memory area */
1706 if (Vad->u.VadFlags.Spare != 0)
1707 {
1708 PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad;
1709 PROS_SECTION_OBJECT Section;
1710
1711 /* Check if it's a section view (RosMm section) */
1712 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1713 {
1714 /* Get the section pointer to the SECTION_OBJECT */
1715 Section = MemoryArea->Data.SectionData.Section;
1716 FileObject = Section->FileObject;
1717 }
1718 else
1719 {
1720 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE);
1721 DPRINT1("VAD is a cache section!\n");
1722 return NULL;
1723 }
1724 }
1725 else
1726 {
1727 /* Make sure it's not a VM VAD */
1728 if (Vad->u.VadFlags.PrivateMemory == 1)
1729 {
1730 DPRINT1("VAD is not a section\n");
1731 return NULL;
1732 }
1733
1734 /* Get the control area */
1735 ControlArea = Vad->ControlArea;
1736 if ((ControlArea == NULL) || !ControlArea->u.Flags.Image)
1737 {
1738 DPRINT1("Address is not a section\n");
1739 return NULL;
1740 }
1741
1742 /* Get the file object */
1743 FileObject = ControlArea->FilePointer;
1744 }
1745
1746 /* Return the file object */
1747 return FileObject;
1748 }
1749
1750 VOID
1751 NTAPI
1752 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation)
1753 {
1754 PSECTION_OBJECT SectionObject;
1755
1756 /* Get the section object of this process*/
1757 SectionObject = PsGetCurrentProcess()->SectionObject;
1758 ASSERT(SectionObject != NULL);
1759 ASSERT(MiIsRosSectionObject(SectionObject) == TRUE);
1760
1761 /* Return the image information */
1762 *ImageInformation = ((PROS_SECTION_OBJECT)SectionObject)->ImageSection->ImageInformation;
1763 }
1764
1765 NTSTATUS
1766 NTAPI
1767 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject,
1768 OUT POBJECT_NAME_INFORMATION *ModuleName)
1769 {
1770 POBJECT_NAME_INFORMATION ObjectNameInfo;
1771 NTSTATUS Status;
1772 ULONG ReturnLength;
1773
1774 /* Allocate memory for our structure */
1775 ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, TAG_MM);
1776 if (!ObjectNameInfo) return STATUS_NO_MEMORY;
1777
1778 /* Query the name */
1779 Status = ObQueryNameString(FileObject,
1780 ObjectNameInfo,
1781 1024,
1782 &ReturnLength);
1783 if (!NT_SUCCESS(Status))
1784 {
1785 /* Failed, free memory */
1786 DPRINT1("Name query failed\n");
1787 ExFreePoolWithTag(ObjectNameInfo, TAG_MM);
1788 *ModuleName = NULL;
1789 return Status;
1790 }
1791
1792 /* Success */
1793 *ModuleName = ObjectNameInfo;
1794 return STATUS_SUCCESS;
1795 }
1796
1797 NTSTATUS
1798 NTAPI
1799 MmGetFileNameForSection(IN PVOID Section,
1800 OUT POBJECT_NAME_INFORMATION *ModuleName)
1801 {
1802 PFILE_OBJECT FileObject;
1803
1804 /* Make sure it's an image section */
1805 if (MiIsRosSectionObject(Section) == FALSE)
1806 {
1807 /* Check ARM3 Section flag */
1808 if (((PSECTION)Section)->u.Flags.Image == 0)
1809 {
1810 /* It's not, fail */
1811 DPRINT1("Not an image section\n");
1812 return STATUS_SECTION_NOT_IMAGE;
1813 }
1814 }
1815 else if (!(((PROS_SECTION_OBJECT)Section)->AllocationAttributes & SEC_IMAGE))
1816 {
1817 /* It's not, fail */
1818 DPRINT1("Not an image section\n");
1819 return STATUS_SECTION_NOT_IMAGE;
1820 }
1821
1822 /* Get the file object */
1823 FileObject = MmGetFileObjectForSection(Section);
1824 return MmGetFileNameForFileObject(FileObject, ModuleName);
1825 }
1826
1827 NTSTATUS
1828 NTAPI
1829 MmGetFileNameForAddress(IN PVOID Address,
1830 OUT PUNICODE_STRING ModuleName)
1831 {
1832 POBJECT_NAME_INFORMATION ModuleNameInformation;
1833 PVOID AddressSpace;
1834 NTSTATUS Status;
1835 PMMVAD Vad;
1836 PFILE_OBJECT FileObject = NULL;
1837
1838 /* Lock address space */
1839 AddressSpace = MmGetCurrentAddressSpace();
1840 MmLockAddressSpace(AddressSpace);
1841
1842 /* Get the VAD */
1843 Vad = MiLocateAddress(Address);
1844 if (Vad == NULL)
1845 {
1846 /* Fail, the address does not exist */
1847 DPRINT1("No VAD at address %p\n", Address);
1848 MmUnlockAddressSpace(AddressSpace);
1849 return STATUS_INVALID_ADDRESS;
1850 }
1851
1852 /* Get the file object pointer for the VAD */
1853 FileObject = MiGetFileObjectForVad(Vad);
1854 if (FileObject == NULL)
1855 {
1856 DPRINT1("Failed to get file object for Address %p\n", Address);
1857 MmUnlockAddressSpace(AddressSpace);
1858 return STATUS_SECTION_NOT_IMAGE;
1859 }
1860
1861 /* Reference the file object */
1862 ObReferenceObject(FileObject);
1863
1864 /* Unlock address space */
1865 MmUnlockAddressSpace(AddressSpace);
1866
1867 /* Get the filename of the file object */
1868 Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation);
1869
1870 /* Dereference the file object */
1871 ObDereferenceObject(FileObject);
1872
1873 /* Check if we were able to get the file object name */
1874 if (NT_SUCCESS(Status))
1875 {
1876 /* Init modulename */
1877 RtlCreateUnicodeString(ModuleName, ModuleNameInformation->Name.Buffer);
1878
1879 /* Free temp taged buffer from MmGetFileNameForFileObject() */
1880 ExFreePoolWithTag(ModuleNameInformation, TAG_MM);
1881 DPRINT("Found ModuleName %S by address %p\n", ModuleName->Buffer, Address);
1882 }
1883
1884 /* Return status */
1885 return Status;
1886 }
1887
1888 NTSTATUS
1889 NTAPI
1890 MiQueryMemorySectionName(IN HANDLE ProcessHandle,
1891 IN PVOID BaseAddress,
1892 OUT PVOID MemoryInformation,
1893 IN SIZE_T MemoryInformationLength,
1894 OUT PSIZE_T ReturnLength)
1895 {
1896 PEPROCESS Process;
1897 NTSTATUS Status;
1898 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1899 UNICODE_STRING ModuleFileName;
1900 PMEMORY_SECTION_NAME SectionName = NULL;
1901 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1902
1903 Status = ObReferenceObjectByHandle(ProcessHandle,
1904 PROCESS_QUERY_INFORMATION,
1905 NULL,
1906 PreviousMode,
1907 (PVOID*)(&Process),
1908 NULL);
1909
1910 if (!NT_SUCCESS(Status))
1911 {
1912 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
1913 return Status;
1914 }
1915
1916 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1917 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
1918
1919 if (NT_SUCCESS(Status))
1920 {
1921 SectionName = MemoryInformation;
1922 if (PreviousMode != KernelMode)
1923 {
1924 _SEH2_TRY
1925 {
1926 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1927 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1928 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1929
1930 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1931
1932 }
1933 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1934 {
1935 Status = _SEH2_GetExceptionCode();
1936 }
1937 _SEH2_END;
1938 }
1939 else
1940 {
1941 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1942 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1943 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1944
1945 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1946
1947 }
1948 }
1949 ObDereferenceObject(Process);
1950 return Status;
1951 }
1952
1953 VOID
1954 NTAPI
1955 MiFlushTbAndCapture(IN PMMVAD FoundVad,
1956 IN PMMPTE PointerPte,
1957 IN ULONG ProtectionMask,
1958 IN PMMPFN Pfn1,
1959 IN BOOLEAN UpdateDirty)
1960 {
1961 MMPTE TempPte, PreviousPte;
1962 KIRQL OldIrql;
1963 BOOLEAN RebuildPte = FALSE;
1964
1965 //
1966 // User for sanity checking later on
1967 //
1968 PreviousPte = *PointerPte;
1969
1970 //
1971 // Build the PTE and acquire the PFN lock
1972 //
1973 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1974 PointerPte,
1975 ProtectionMask,
1976 PreviousPte.u.Hard.PageFrameNumber);
1977 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1978
1979 //
1980 // We don't support I/O mappings in this path yet
1981 //
1982 ASSERT(Pfn1 != NULL);
1983 ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined);
1984
1985 //
1986 // Make sure new protection mask doesn't get in conflict and fix it if it does
1987 //
1988 if (Pfn1->u3.e1.CacheAttribute == MiCached)
1989 {
1990 //
1991 // This is a cached PFN
1992 //
1993 if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS))
1994 {
1995 RebuildPte = TRUE;
1996 ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS);
1997 }
1998 }
1999 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
2000 {
2001 //
2002 // This is a non-cached PFN
2003 //
2004 if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE)
2005 {
2006 RebuildPte = TRUE;
2007 ProtectionMask &= ~MM_NOACCESS;
2008 ProtectionMask |= MM_NOCACHE;
2009 }
2010 }
2011
2012 if (RebuildPte)
2013 {
2014 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2015 PointerPte,
2016 ProtectionMask,
2017 PreviousPte.u.Hard.PageFrameNumber);
2018 }
2019
2020 //
2021 // Write the new PTE, making sure we are only changing the bits
2022 //
2023 MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2024
2025 //
2026 // Flush the TLB
2027 //
2028 ASSERT(PreviousPte.u.Hard.Valid == 1);
2029 KeFlushCurrentTb();
2030 ASSERT(PreviousPte.u.Hard.Valid == 1);
2031
2032 //
2033 // Windows updates the relevant PFN1 information, we currently don't.
2034 //
2035 if (UpdateDirty && PreviousPte.u.Hard.Dirty)
2036 {
2037 if (!Pfn1->u3.e1.Modified)
2038 {
2039 DPRINT1("FIXME: Mark PFN as dirty\n");
2040 }
2041 }
2042
2043 //
2044 // Not supported in ARM3
2045 //
2046 ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch);
2047
2048 //
2049 // Release the PFN lock, we are done
2050 //
2051 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2052 }
2053
2054 //
2055 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
2056 //
2057 NTSTATUS
2058 NTAPI
2059 MiSetProtectionOnSection(IN PEPROCESS Process,
2060 IN PMMVAD FoundVad,
2061 IN PVOID StartingAddress,
2062 IN PVOID EndingAddress,
2063 IN ULONG NewProtect,
2064 OUT PULONG CapturedOldProtect,
2065 IN ULONG DontCharge,
2066 OUT PULONG Locked)
2067 {
2068 PMMPTE PointerPte, LastPte;
2069 MMPTE TempPte, PteContents;
2070 PMMPDE PointerPde;
2071 PMMPFN Pfn1;
2072 ULONG ProtectionMask, QuotaCharge = 0;
2073 PETHREAD Thread = PsGetCurrentThread();
2074 PAGED_CODE();
2075
2076 //
2077 // Tell caller nothing is being locked
2078 //
2079 *Locked = FALSE;
2080
2081 //
2082 // This function should only be used for section VADs. Windows ASSERT */
2083 //
2084 ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0);
2085
2086 //
2087 // We don't support these features in ARM3
2088 //
2089 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2090 ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0);
2091
2092 //
2093 // Convert and validate the protection mask
2094 //
2095 ProtectionMask = MiMakeProtectionMask(NewProtect);
2096 if (ProtectionMask == MM_INVALID_PROTECTION)
2097 {
2098 DPRINT1("Invalid section protect\n");
2099 return STATUS_INVALID_PAGE_PROTECTION;
2100 }
2101
2102 //
2103 // Get the PTE and PDE for the address, as well as the final PTE
2104 //
2105 MiLockProcessWorkingSetUnsafe(Process, Thread);
2106 PointerPde = MiAddressToPde(StartingAddress);
2107 PointerPte = MiAddressToPte(StartingAddress);
2108 LastPte = MiAddressToPte(EndingAddress);
2109
2110 //
2111 // Make the PDE valid, and check the status of the first PTE
2112 //
2113 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2114 if (PointerPte->u.Long)
2115 {
2116 //
2117 // Not supported in ARM3
2118 //
2119 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
2120
2121 //
2122 // Capture the page protection and make the PDE valid
2123 //
2124 *CapturedOldProtect = MiGetPageProtection(PointerPte);
2125 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2126 }
2127 else
2128 {
2129 //
2130 // Only pagefile-backed section VADs are supported for now
2131 //
2132 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2133
2134 //
2135 // Grab the old protection from the VAD itself
2136 //
2137 *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection];
2138 }
2139
2140 //
2141 // Loop all the PTEs now
2142 //
2143 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2144 while (PointerPte <= LastPte)
2145 {
2146 //
2147 // Check if we've crossed a PDE boundary and make the new PDE valid too
2148 //
2149 if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
2150 {
2151 PointerPde = MiPteToPde(PointerPte);
2152 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2153 }
2154
2155 //
2156 // Capture the PTE and see what we're dealing with
2157 //
2158 PteContents = *PointerPte;
2159 if (PteContents.u.Long == 0)
2160 {
2161 //
2162 // This used to be a zero PTE and it no longer is, so we must add a
2163 // reference to the pagetable.
2164 //
2165 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2166
2167 //
2168 // Create the demand-zero prototype PTE
2169 //
2170 TempPte = PrototypePte;
2171 TempPte.u.Soft.Protection = ProtectionMask;
2172 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2173 }
2174 else if (PteContents.u.Hard.Valid == 1)
2175 {
2176 //
2177 // Get the PFN entry
2178 //
2179 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2180
2181 //
2182 // We don't support these yet
2183 //
2184 ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0);
2185 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2186
2187 //
2188 // Write the protection mask and write it with a TLB flush
2189 //
2190 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2191 MiFlushTbAndCapture(FoundVad,
2192 PointerPte,
2193 ProtectionMask,
2194 Pfn1,
2195 TRUE);
2196 }
2197 else
2198 {
2199 //
2200 // We don't support these cases yet
2201 //
2202 ASSERT(PteContents.u.Soft.Prototype == 0);
2203 ASSERT(PteContents.u.Soft.Transition == 0);
2204
2205 //
2206 // The PTE is already demand-zero, just update the protection mask
2207 //
2208 PointerPte->u.Soft.Protection = ProtectionMask;
2209 }
2210
2211 PointerPte++;
2212 }
2213
2214 //
2215 // Unlock the working set and update quota charges if needed, then return
2216 //
2217 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2218 if ((QuotaCharge > 0) && (!DontCharge))
2219 {
2220 FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
2221 Process->CommitCharge -= QuotaCharge;
2222 }
2223 return STATUS_SUCCESS;
2224 }
2225
2226 VOID
2227 NTAPI
2228 MiRemoveMappedPtes(IN PVOID BaseAddress,
2229 IN ULONG NumberOfPtes,
2230 IN PCONTROL_AREA ControlArea,
2231 IN PMMSUPPORT Ws)
2232 {
2233 PMMPTE PointerPte, ProtoPte;//, FirstPte;
2234 PMMPDE PointerPde, SystemMapPde;
2235 PMMPFN Pfn1, Pfn2;
2236 MMPTE PteContents;
2237 KIRQL OldIrql;
2238 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress);
2239
2240 ASSERT(Ws == NULL);
2241
2242 /* Get the PTE and loop each one */
2243 PointerPte = MiAddressToPte(BaseAddress);
2244 //FirstPte = PointerPte;
2245 while (NumberOfPtes)
2246 {
2247 /* Check if the PTE is already valid */
2248 PteContents = *PointerPte;
2249 if (PteContents.u.Hard.Valid == 1)
2250 {
2251 /* Get the PFN entry */
2252 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2253
2254 /* Get the PTE */
2255 PointerPde = MiPteToPde(PointerPte);
2256
2257 /* Lock the PFN database and make sure this isn't a mapped file */
2258 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2259 ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0);
2260
2261 /* Mark the page as modified accordingly */
2262 if (MI_IS_PAGE_DIRTY(&PteContents))
2263 Pfn1->u3.e1.Modified = 1;
2264
2265 /* Was the PDE invalid */
2266 if (PointerPde->u.Long == 0)
2267 {
2268 #if (_MI_PAGING_LEVELS == 2)
2269 /* Find the system double-mapped PDE that describes this mapping */
2270 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
2271
2272 /* Make it valid */
2273 ASSERT(SystemMapPde->u.Hard.Valid == 1);
2274 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde);
2275 #else
2276 DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde);
2277 ASSERT(FALSE);
2278 #endif
2279 }
2280
2281 /* Dereference the PDE and the PTE */
2282 Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
2283 MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2284 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
2285 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2286
2287 /* Release the PFN lock */
2288 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2289 }
2290 else
2291 {
2292 /* Windows ASSERT */
2293 ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1));
2294
2295 /* Check if this is a prototype pointer PTE */
2296 if (PteContents.u.Soft.Prototype == 1)
2297 {
2298 /* Get the prototype PTE */
2299 ProtoPte = MiProtoPteToPte(&PteContents);
2300
2301 /* We don't support anything else atm */
2302 ASSERT(ProtoPte->u.Long == 0);
2303 }
2304 }
2305
2306 /* Make the PTE into a zero PTE */
2307 PointerPte->u.Long = 0;
2308
2309 /* Move to the next PTE */
2310 PointerPte++;
2311 NumberOfPtes--;
2312 }
2313
2314 /* Flush the TLB */
2315 KeFlushCurrentTb();
2316
2317 /* Acquire the PFN lock */
2318 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2319
2320 /* Decrement the accounting counters */
2321 ControlArea->NumberOfUserReferences--;
2322 ControlArea->NumberOfMappedViews--;
2323
2324 /* Check if we should destroy the CA and release the lock */
2325 MiCheckControlArea(ControlArea, OldIrql);
2326 }
2327
2328 ULONG
2329 NTAPI
2330 MiRemoveFromSystemSpace(IN PMMSESSION Session,
2331 IN PVOID Base,
2332 OUT PCONTROL_AREA *ControlArea)
2333 {
2334 ULONG Hash, Size, Count = 0;
2335 ULONG_PTR Entry;
2336 PAGED_CODE();
2337
2338 /* Compute the hash for this entry and loop trying to find it */
2339 Entry = (ULONG_PTR)Base >> 16;
2340 Hash = Entry % Session->SystemSpaceHashKey;
2341 while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry)
2342 {
2343 /* Check if we overflew past the end of the hash table */
2344 if (++Hash >= Session->SystemSpaceHashSize)
2345 {
2346 /* Reset the hash to zero and keep searching from the bottom */
2347 Hash = 0;
2348 if (++Count == 2)
2349 {
2350 /* But if we overflew twice, then this is not a real mapping */
2351 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2352 (ULONG_PTR)Base,
2353 1,
2354 0,
2355 0);
2356 }
2357 }
2358 }
2359
2360 /* One less entry */
2361 Session->SystemSpaceHashEntries--;
2362
2363 /* Extract the size and clear the entry */
2364 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2365 Session->SystemSpaceViewTable[Hash].Entry = 0;
2366
2367 /* Return the control area and the size */
2368 *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2369 return Size;
2370 }
2371
2372 NTSTATUS
2373 NTAPI
2374 MiUnmapViewInSystemSpace(IN PMMSESSION Session,
2375 IN PVOID MappedBase)
2376 {
2377 ULONG Size;
2378 PCONTROL_AREA ControlArea;
2379 PAGED_CODE();
2380
2381 /* Remove this mapping */
2382 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2383 Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
2384
2385 /* Clear the bits for this mapping */
2386 RtlClearBits(Session->SystemSpaceBitMap,
2387 (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16),
2388 Size);
2389
2390 /* Convert the size from a bit size into the actual size */
2391 Size = Size * (_64K >> PAGE_SHIFT);
2392
2393 /* Remove the PTEs now */
2394 MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL);
2395 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2396
2397 /* Return success */
2398 return STATUS_SUCCESS;
2399 }
2400
2401 /* PUBLIC FUNCTIONS ***********************************************************/
2402
2403 /*
2404 * @implemented
2405 */
2406 NTSTATUS
2407 NTAPI
2408 MmCreateArm3Section(OUT PVOID *SectionObject,
2409 IN ACCESS_MASK DesiredAccess,
2410 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2411 IN PLARGE_INTEGER InputMaximumSize,
2412 IN ULONG SectionPageProtection,
2413 IN ULONG AllocationAttributes,
2414 IN HANDLE FileHandle OPTIONAL,
2415 IN PFILE_OBJECT FileObject OPTIONAL)
2416 {
2417 SECTION Section;
2418 PSECTION NewSection;
2419 PSUBSECTION Subsection;
2420 PSEGMENT NewSegment, Segment;
2421 NTSTATUS Status;
2422 PCONTROL_AREA ControlArea;
2423 ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge;
2424 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2425 BOOLEAN FileLock = FALSE, KernelCall = FALSE;
2426 KIRQL OldIrql;
2427 PFILE_OBJECT File;
2428 BOOLEAN UserRefIncremented = FALSE;
2429 PVOID PreviousSectionPointer;
2430
2431 /* Make the same sanity checks that the Nt interface should've validated */
2432 ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
2433 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
2434 SEC_NO_CHANGE)) == 0);
2435 ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
2436 ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
2437 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
2438 SEC_NOCACHE | SEC_NO_CHANGE))));
2439 ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
2440 ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
2441 (SectionPageProtection & PAGE_WRITECOMBINE) ||
2442 (SectionPageProtection & PAGE_GUARD) ||
2443 (SectionPageProtection & PAGE_NOACCESS)));
2444
2445 /* Convert section flag to page flag */
2446 if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
2447
2448 /* Check to make sure the protection is correct. Nt* does this already */
2449 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
2450 if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
2451
2452 /* Check if this is going to be a data or image backed file section */
2453 if ((FileHandle) || (FileObject))
2454 {
2455 /* These cannot be mapped with large pages */
2456 if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6;
2457
2458 /* For now, only support the mechanism through a file handle */
2459 ASSERT(FileObject == NULL);
2460
2461 /* Reference the file handle to get the object */
2462 Status = ObReferenceObjectByHandle(FileHandle,
2463 MmMakeFileAccess[ProtectionMask],
2464 IoFileObjectType,
2465 PreviousMode,
2466 (PVOID*)&File,
2467 NULL);
2468 if (!NT_SUCCESS(Status)) return Status;
2469
2470 /* Make sure Cc has been doing its job */
2471 if (!File->SectionObjectPointer)
2472 {
2473 /* This is not a valid file system-based file, fail */
2474 ObDereferenceObject(File);
2475 return STATUS_INVALID_FILE_FOR_SECTION;
2476 }
2477
2478 /* Image-file backed sections are not yet supported */
2479 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2480
2481 /* Compute the size of the control area, and allocate it */
2482 ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
2483 ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM');
2484 if (!ControlArea)
2485 {
2486 ObDereferenceObject(File);
2487 return STATUS_INSUFFICIENT_RESOURCES;
2488 }
2489
2490 /* Zero it out */
2491 RtlZeroMemory(ControlArea, ControlAreaSize);
2492
2493 /* Did we get a handle, or an object? */
2494 if (FileHandle)
2495 {
2496 /* We got a file handle so we have to lock down the file */
2497 #if 0
2498 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection);
2499 if (!NT_SUCCESS(Status))
2500 {
2501 ExFreePool(ControlArea);
2502 ObDereferenceObject(File);
2503 return Status;
2504 }
2505 #else
2506 /* ReactOS doesn't support this API yet, so do nothing */
2507 Status = STATUS_SUCCESS;
2508 #endif
2509 /* Update the top-level IRP so that drivers know what's happening */
2510 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
2511 FileLock = TRUE;
2512 }
2513
2514 /* Lock the PFN database while we play with the section pointers */
2515 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2516
2517 /* Image-file backed sections are not yet supported */
2518 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2519
2520 /* There should not already be a control area for this file */
2521 ASSERT(File->SectionObjectPointer->DataSectionObject == NULL);
2522 NewSegment = NULL;
2523
2524 /* Write down that this CA is being created, and set it */
2525 ControlArea->u.Flags.BeingCreated = TRUE;
2526 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2527 PreviousSectionPointer = File->SectionObjectPointer;
2528 File->SectionObjectPointer->DataSectionObject = ControlArea;
2529
2530 /* We can release the PFN lock now */
2531 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2532
2533 /* We don't support previously-mapped file */
2534 ASSERT(NewSegment == NULL);
2535
2536 /* Image-file backed sections are not yet supported */
2537 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2538
2539 /* So we always create a data file map */
2540 Status = MiCreateDataFileMap(File,
2541 &Segment,
2542 (PSIZE_T)InputMaximumSize,
2543 SectionPageProtection,
2544 AllocationAttributes,
2545 KernelCall);
2546 if (!NT_SUCCESS(Status))
2547 {
2548 /* Lock the PFN database while we play with the section pointers */
2549 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2550
2551 /* Reset the waiting-for-deletion event */
2552 ASSERT(ControlArea->WaitingForDeletion == NULL);
2553 ControlArea->WaitingForDeletion = NULL;
2554
2555 /* Set the file pointer NULL flag */
2556 ASSERT(ControlArea->u.Flags.FilePointerNull == 0);
2557 ControlArea->u.Flags.FilePointerNull = TRUE;
2558
2559 /* Delete the data section object */
2560 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2561 File->SectionObjectPointer->DataSectionObject = NULL;
2562
2563 /* No longer being created */
2564 ControlArea->u.Flags.BeingCreated = FALSE;
2565
2566 /* We can release the PFN lock now */
2567 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2568
2569 /* Check if we locked and set the IRP */
2570 if (FileLock)
2571 {
2572 /* Undo */
2573 IoSetTopLevelIrp(NULL);
2574 //FsRtlReleaseFile(File);
2575 }
2576
2577 /* Free the control area and de-ref the file object */
2578 ExFreePool(ControlArea);
2579 ObDereferenceObject(File);
2580
2581 /* All done */
2582 return Status;
2583 }
2584
2585 /* On success, we expect this */
2586 ASSERT(PreviousSectionPointer == File->SectionObjectPointer);
2587
2588 /* Check if a maximum size was specified */
2589 if (!InputMaximumSize->QuadPart)
2590 {
2591 /* Nope, use the segment size */
2592 Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
2593 }
2594 else
2595 {
2596 /* Yep, use the entered size */
2597 Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart;
2598 }
2599 }
2600 else
2601 {
2602 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2603 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
2604
2605 /* Not yet supported */
2606 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
2607
2608 /* So this must be a pagefile-backed section, create the mappings needed */
2609 Status = MiCreatePagingFileMap(&NewSegment,
2610 (PSIZE_T)InputMaximumSize,
2611 ProtectionMask,
2612 AllocationAttributes);
2613 if (!NT_SUCCESS(Status)) return Status;
2614
2615 /* Set the size here, and read the control area */
2616 Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
2617 ControlArea = NewSegment->ControlArea;
2618
2619 /* MiCreatePagingFileMap increments user references */
2620 UserRefIncremented = TRUE;
2621 }
2622
2623 /* Did we already have a segment? */
2624 if (!NewSegment)
2625 {
2626 /* This must be the file path and we created a segment */
2627 NewSegment = Segment;
2628 ASSERT(File != NULL);
2629
2630 /* Acquire the PFN lock while we set control area flags */
2631 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2632
2633 /* We don't support this race condition yet, so assume no waiters */
2634 ASSERT(ControlArea->WaitingForDeletion == NULL);
2635 ControlArea->WaitingForDeletion = NULL;
2636
2637 /* Image-file backed sections are not yet supported, nor ROM images */
2638 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2639 ASSERT(Segment->ControlArea->u.Flags.Rom == 0);
2640
2641 /* Take off the being created flag, and then release the lock */
2642 ControlArea->u.Flags.BeingCreated = FALSE;
2643 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2644 }
2645
2646 /* Check if we locked the file earlier */
2647 if (FileLock)
2648 {
2649 /* Reset the top-level IRP and release the lock */
2650 IoSetTopLevelIrp(NULL);
2651 //FsRtlReleaseFile(File);
2652 FileLock = FALSE;
2653 }
2654
2655 /* Set the initial section object data */
2656 Section.InitialPageProtection = SectionPageProtection;
2657
2658 /* The mapping created a control area and segment, save the flags */
2659 Section.Segment = NewSegment;
2660 Section.u.LongFlags = ControlArea->u.LongFlags;
2661
2662 /* Check if this is a user-mode read-write non-image file mapping */
2663 if (!(FileObject) &&
2664 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2665 !(ControlArea->u.Flags.Image) &&
2666 (ControlArea->FilePointer))
2667 {
2668 /* Add a reference and set the flag */
2669 Section.u.Flags.UserWritable = TRUE;
2670 InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences);
2671 }
2672
2673 /* Check for image mappings or page file mappings */
2674 if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer))
2675 {
2676 /* Charge the segment size, and allocate a subsection */
2677 PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
2678 Size = sizeof(SUBSECTION);
2679 }
2680 else
2681 {
2682 /* Charge nothing, and allocate a mapped subsection */
2683 PagedCharge = 0;
2684 Size = sizeof(MSUBSECTION);
2685 }
2686
2687 /* Check if this is a normal CA */
2688 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2689 ASSERT(ControlArea->u.Flags.Rom == 0);
2690
2691 /* Charge only a CA, and the subsection is right after */
2692 NonPagedCharge = sizeof(CONTROL_AREA);
2693 Subsection = (PSUBSECTION)(ControlArea + 1);
2694
2695 /* We only support single-subsection mappings */
2696 NonPagedCharge += Size;
2697 ASSERT(Subsection->NextSubsection == NULL);
2698
2699 /* Create the actual section object, with enough space for the prototype PTEs */
2700 Status = ObCreateObject(PreviousMode,
2701 MmSectionObjectType,
2702 ObjectAttributes,
2703 PreviousMode,
2704 NULL,
2705 sizeof(SECTION),
2706 PagedCharge,
2707 NonPagedCharge,
2708 (PVOID*)&NewSection);
2709 if (!NT_SUCCESS(Status))
2710 {
2711 /* Check if this is a user-mode read-write non-image file mapping */
2712 if (!(FileObject) &&
2713 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2714 !(ControlArea->u.Flags.Image) &&
2715 (ControlArea->FilePointer))
2716 {
2717 /* Remove a reference and check the flag */
2718 ASSERT(Section.u.Flags.UserWritable == 1);
2719 InterlockedDecrement((volatile LONG*)&ControlArea->WritableUserReferences);
2720 }
2721
2722 /* Check if a user reference was added */
2723 if (UserRefIncremented)
2724 {
2725 /* Acquire the PFN lock while we change counters */
2726 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2727
2728 /* Decrement the accounting counters */
2729 ControlArea->NumberOfSectionReferences--;
2730 ASSERT((LONG)ControlArea->NumberOfUserReferences > 0);
2731 ControlArea->NumberOfUserReferences--;
2732
2733 /* Check if we should destroy the CA and release the lock */
2734 MiCheckControlArea(ControlArea, OldIrql);
2735 }
2736
2737 /* Return the failure code */
2738 return Status;
2739 }
2740
2741 /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */
2742
2743 /* Now copy the local section object from the stack into this new object */
2744 RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
2745 NewSection->Address.StartingVpn = 0;
2746
2747 /* For now, only user calls are supported */
2748 ASSERT(KernelCall == FALSE);
2749 NewSection->u.Flags.UserReference = TRUE;
2750
2751 /* Is this a "based" allocation, in which all mappings are identical? */
2752 if (AllocationAttributes & SEC_BASED)
2753 {
2754 /* Lock the VAD tree during the search */
2755 KeAcquireGuardedMutex(&MmSectionBasedMutex);
2756
2757 /* Is it a brand new ControArea ? */
2758 if (ControlArea->u.Flags.BeingCreated == 1)
2759 {
2760 ASSERT(ControlArea->u.Flags.Based == 1);
2761 /* Then we must find a global address, top-down */
2762 Status = MiFindEmptyAddressRangeDownBasedTree((SIZE_T)ControlArea->Segment->SizeOfSegment,
2763 (ULONG_PTR)MmHighSectionBase,
2764 _64K,
2765 &MmSectionBasedRoot,
2766 (ULONG_PTR*)&ControlArea->Segment->BasedAddress);
2767
2768 if (!NT_SUCCESS(Status))
2769 {
2770 /* No way to find a valid range. */
2771 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2772 ControlArea->u.Flags.Based = 0;
2773 NewSection->u.Flags.Based = 0;
2774 ObDereferenceObject(NewSection);
2775 return Status;
2776 }
2777
2778 /* Compute the ending address and insert it into the VAD tree */
2779 NewSection->Address.StartingVpn = (ULONG_PTR)ControlArea->Segment->BasedAddress;
2780 NewSection->Address.EndingVpn = NewSection->Address.StartingVpn + NewSection->SizeOfSection.LowPart - 1;
2781 MiInsertBasedSection(NewSection);
2782 }
2783 else
2784 {
2785 /* FIXME : Should we deny section creation if SEC_BASED is not set ? Can we have two different section objects on the same based address ? Investigate !*/
2786 ASSERT(FALSE);
2787 }
2788
2789 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2790 }
2791
2792 /* The control area is not being created anymore */
2793 if (ControlArea->u.Flags.BeingCreated == 1)
2794 {
2795 /* Acquire the PFN lock while we set control area flags */
2796 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2797
2798 /* Take off the being created flag, and then release the lock */
2799 ControlArea->u.Flags.BeingCreated = 0;
2800 NewSection->u.Flags.BeingCreated = 0;
2801
2802 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2803 }
2804
2805 /* Migrate the attribute into a flag */
2806 if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE;
2807
2808 /* If R/W access is not requested, this might eventually become a CoW mapping */
2809 if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
2810 {
2811 NewSection->u.Flags.CopyOnWrite = TRUE;
2812 }
2813
2814 /* Write down if this was a kernel call */
2815 ControlArea->u.Flags.WasPurged |= KernelCall;
2816 ASSERT(ControlArea->u.Flags.WasPurged == FALSE);
2817
2818 /* Make sure the segment and the section are the same size, or the section is smaller */
2819 ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment);
2820
2821 /* Return the object and the creation status */
2822 *SectionObject = (PVOID)NewSection;
2823 return Status;
2824 }
2825
2826 /*
2827 * @implemented
2828 */
2829 NTSTATUS
2830 NTAPI
2831 MmMapViewOfArm3Section(IN PVOID SectionObject,
2832 IN PEPROCESS Process,
2833 IN OUT PVOID *BaseAddress,
2834 IN ULONG_PTR ZeroBits,
2835 IN SIZE_T CommitSize,
2836 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
2837 IN OUT PSIZE_T ViewSize,
2838 IN SECTION_INHERIT InheritDisposition,
2839 IN ULONG AllocationType,
2840 IN ULONG Protect)
2841 {
2842 KAPC_STATE ApcState;
2843 BOOLEAN Attached = FALSE;
2844 PSECTION Section;
2845 PCONTROL_AREA ControlArea;
2846 ULONG ProtectionMask;
2847 NTSTATUS Status;
2848 PAGED_CODE();
2849
2850 /* Get the segment and control area */
2851 Section = (PSECTION)SectionObject;
2852 ControlArea = Section->Segment->ControlArea;
2853
2854 /* These flags/states are not yet supported by ARM3 */
2855 ASSERT(Section->u.Flags.Image == 0);
2856 ASSERT(Section->u.Flags.NoCache == 0);
2857 ASSERT(Section->u.Flags.WriteCombined == 0);
2858 ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2859
2860 /* FIXME */
2861 if ((AllocationType & MEM_RESERVE) != 0)
2862 {
2863 DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n");
2864 return STATUS_NOT_IMPLEMENTED;
2865 }
2866
2867 /* Check if the mapping protection is compatible with the create */
2868 if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2869 {
2870 DPRINT1("Mapping protection is incompatible\n");
2871 return STATUS_SECTION_PROTECTION;
2872 }
2873
2874 /* Check if the offset and size would cause an overflow */
2875 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2876 (ULONG64)SectionOffset->QuadPart)
2877 {
2878 DPRINT1("Section offset overflows\n");
2879 return STATUS_INVALID_VIEW_SIZE;
2880 }
2881
2882 /* Check if the offset and size are bigger than the section itself */
2883 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2884 (ULONG64)Section->SizeOfSection.QuadPart)
2885 {
2886 DPRINT1("Section offset is larger than section\n");
2887 return STATUS_INVALID_VIEW_SIZE;
2888 }
2889
2890 /* Check if the caller did not specify a view size */
2891 if (!(*ViewSize))
2892 {
2893 /* Compute it for the caller */
2894 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
2895
2896 /* Check if it's larger than 4GB or overflows into kernel-mode */
2897 if ((*ViewSize > 0xFFFFFFFF) ||
2898 (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize))
2899 {
2900 DPRINT1("Section view won't fit\n");
2901 return STATUS_INVALID_VIEW_SIZE;
2902 }
2903 }
2904
2905 /* Check if the commit size is larger than the view size */
2906 if (CommitSize > *ViewSize)
2907 {
2908 DPRINT1("Attempting to commit more than the view itself\n");
2909 return STATUS_INVALID_PARAMETER_5;
2910 }
2911
2912 /* Check if the view size is larger than the section */
2913 if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2914 {
2915 DPRINT1("The view is larger than the section\n");
2916 return STATUS_INVALID_VIEW_SIZE;
2917 }
2918
2919 /* Compute and validate the protection mask */
2920 ProtectionMask = MiMakeProtectionMask(Protect);
2921 if (ProtectionMask == MM_INVALID_PROTECTION)
2922 {
2923 DPRINT1("The protection is invalid\n");
2924 return STATUS_INVALID_PAGE_PROTECTION;
2925 }
2926
2927 /* We only handle pagefile-backed sections, which cannot be writecombined */
2928 if (Protect & PAGE_WRITECOMBINE)
2929 {
2930 DPRINT1("Cannot write combine a pagefile-backed section\n");
2931 return STATUS_INVALID_PARAMETER_10;
2932 }
2933
2934 /* Start by attaching to the current process if needed */
2935 if (PsGetCurrentProcess() != Process)
2936 {
2937 KeStackAttachProcess(&Process->Pcb, &ApcState);
2938 Attached = TRUE;
2939 }
2940
2941 /* Do the actual mapping */
2942 Status = MiMapViewOfDataSection(ControlArea,
2943 Process,
2944 BaseAddress,
2945 SectionOffset,
2946 ViewSize,
2947 Section,
2948 InheritDisposition,
2949 ProtectionMask,
2950 CommitSize,
2951 ZeroBits,
2952 AllocationType);
2953
2954 /* Detach if needed, then return status */
2955 if (Attached) KeUnstackDetachProcess(&ApcState);
2956 return Status;
2957 }
2958
2959 /*
2960 * @unimplemented
2961 */
2962 BOOLEAN
2963 NTAPI
2964 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2965 {
2966 UNIMPLEMENTED;
2967 return FALSE;
2968 }
2969
2970 /*
2971 * @unimplemented
2972 */
2973 BOOLEAN
2974 NTAPI
2975 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2976 IN BOOLEAN DelayClose)
2977 {
2978 UNIMPLEMENTED;
2979 return FALSE;
2980 }
2981
2982 /*
2983 * @implemented
2984 */
2985 NTSTATUS
2986 NTAPI
2987 MmMapViewInSessionSpace(IN PVOID Section,
2988 OUT PVOID *MappedBase,
2989 IN OUT PSIZE_T ViewSize)
2990 {
2991 PAGED_CODE();
2992
2993 // HACK
2994 if (MiIsRosSectionObject(Section))
2995 {
2996 return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
2997 }
2998
2999 /* Process must be in a session */
3000 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3001 {
3002 DPRINT1("Process is not in session\n");
3003 return STATUS_NOT_MAPPED_VIEW;
3004 }
3005
3006 /* Use the system space API, but with the session view instead */
3007 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3008 return MiMapViewInSystemSpace(Section,
3009 &MmSessionSpace->Session,
3010 MappedBase,
3011 ViewSize);
3012 }
3013
3014 /*
3015 * @implemented
3016 */
3017 NTSTATUS
3018 NTAPI
3019 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
3020 {
3021 PAGED_CODE();
3022
3023 // HACK
3024 if (!MI_IS_SESSION_ADDRESS(MappedBase))
3025 {
3026 return MmUnmapViewInSystemSpace(MappedBase);
3027 }
3028
3029 /* Process must be in a session */
3030 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3031 {
3032 DPRINT1("Proess is not in session\n");
3033 return STATUS_NOT_MAPPED_VIEW;
3034 }
3035
3036 /* Use the system space API, but with the session view instead */
3037 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3038 return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
3039 MappedBase);
3040 }
3041
3042 /*
3043 * @implemented
3044 */
3045 NTSTATUS
3046 NTAPI
3047 MmUnmapViewOfSection(IN PEPROCESS Process,
3048 IN PVOID BaseAddress)
3049 {
3050 return MiUnmapViewOfSection(Process, BaseAddress, 0);
3051 }
3052
3053 /*
3054 * @implemented
3055 */
3056 NTSTATUS
3057 NTAPI
3058 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
3059 {
3060 PMEMORY_AREA MemoryArea;
3061 PAGED_CODE();
3062
3063 /* Was this mapped by RosMm? */
3064 MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
3065 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
3066 {
3067 return MiRosUnmapViewInSystemSpace(MappedBase);
3068 }
3069
3070 /* It was not, call the ARM3 routine */
3071 return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
3072 }
3073
3074 /*
3075 * @implemented
3076 */
3077 NTSTATUS
3078 NTAPI
3079 MmCommitSessionMappedView(IN PVOID MappedBase,
3080 IN SIZE_T ViewSize)
3081 {
3082 ULONG_PTR StartAddress, EndingAddress, Base;
3083 ULONG Hash, Count = 0, Size, QuotaCharge;
3084 PMMSESSION Session;
3085 PMMPTE LastProtoPte, PointerPte, ProtoPte;
3086 PCONTROL_AREA ControlArea;
3087 PSEGMENT Segment;
3088 PSUBSECTION Subsection;
3089 MMPTE TempPte;
3090 PAGED_CODE();
3091
3092 /* Make sure the base isn't past the session view range */
3093 if ((MappedBase < MiSessionViewStart) ||
3094 (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
3095 {
3096 DPRINT1("Base outside of valid range\n");
3097 return STATUS_INVALID_PARAMETER_1;
3098 }
3099
3100 /* Make sure the size isn't past the session view range */
3101 if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
3102 (ULONG_PTR)MappedBase) < ViewSize)
3103 {
3104 DPRINT1("Size outside of valid range\n");
3105 return STATUS_INVALID_PARAMETER_2;
3106 }
3107
3108 /* Sanity check */
3109 ASSERT(ViewSize != 0);
3110
3111 /* Process must be in a session */
3112 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3113 {
3114 DPRINT1("Process is not in session\n");
3115 return STATUS_NOT_MAPPED_VIEW;
3116 }
3117
3118 /* Compute the correctly aligned base and end addresses */
3119 StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
3120 EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
3121
3122 /* Sanity check and grab the session */
3123 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3124 Session = &MmSessionSpace->Session;
3125
3126 /* Get the hash entry for this allocation */
3127 Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
3128
3129 /* Lock system space */
3130 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
3131
3132 /* Loop twice so we can try rolling over if needed */
3133 while (TRUE)
3134 {
3135 /* Extract the size and base addresses from the entry */
3136 Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
3137 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
3138
3139 /* Convert the size to bucket chunks */
3140 Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
3141
3142 /* Bail out if this entry fits in here */
3143 if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
3144
3145 /* Check if we overflew past the end of the hash table */
3146 if (++Hash >= Session->SystemSpaceHashSize)
3147 {
3148 /* Reset the hash to zero and keep searching from the bottom */
3149 Hash = 0;
3150 if (++Count == 2)
3151 {
3152 /* But if we overflew twice, then this is not a real mapping */
3153 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
3154 Base,
3155 2,
3156 0,
3157 0);
3158 }
3159 }
3160 }
3161
3162 /* Make sure the view being mapped is not file-based */
3163 ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
3164 if (ControlArea->FilePointer != NULL)
3165 {
3166 /* It is, so we have to bail out */
3167 DPRINT1("Only page-filed backed sections can be commited\n");
3168 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3169 return STATUS_ALREADY_COMMITTED;
3170 }
3171
3172 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
3173 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
3174 ASSERT(ControlArea->u.Flags.Rom == 0);
3175 Subsection = (PSUBSECTION)(ControlArea + 1);
3176
3177 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
3178 ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
3179 QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
3180 LastProtoPte = ProtoPte + QuotaCharge;
3181 if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
3182 {
3183 DPRINT1("PTE is out of bounds\n");
3184 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3185 return STATUS_INVALID_PARAMETER_2;
3186 }
3187
3188 /* Acquire the commit lock and count all the non-committed PTEs */
3189 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
3190 PointerPte = ProtoPte;
3191 while (PointerPte < LastProtoPte)
3192 {
3193 if (PointerPte->u.Long) QuotaCharge--;
3194 PointerPte++;
3195 }
3196
3197 /* Was everything committed already? */
3198 if (!QuotaCharge)
3199 {
3200 /* Nothing to do! */
3201 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3202 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3203 return STATUS_SUCCESS;
3204 }
3205
3206 /* Pick the segment and template PTE */
3207 Segment = ControlArea->Segment;
3208 TempPte = Segment->SegmentPteTemplate;
3209 ASSERT(TempPte.u.Long != 0);
3210
3211 /* Loop all prototype PTEs to be committed */
3212 PointerPte = ProtoPte;
3213 while (PointerPte < LastProtoPte)
3214 {
3215 /* Make sure the PTE is already invalid */
3216 if (PointerPte->u.Long == 0)
3217 {
3218 /* And write the invalid PTE */
3219 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3220 }
3221
3222 /* Move to the next PTE */
3223 PointerPte++;
3224 }
3225
3226 /* Check if we had at least one page charged */
3227 if (QuotaCharge)
3228 {
3229 /* Update the accounting data */
3230 Segment->NumberOfCommittedPages += QuotaCharge;
3231 InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3232 }
3233
3234 /* Release all */
3235 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3236 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3237 return STATUS_SUCCESS;
3238 }
3239
3240 VOID
3241 NTAPI
3242 MiDeleteARM3Section(PVOID ObjectBody)
3243 {
3244 PSECTION SectionObject;
3245 PCONTROL_AREA ControlArea;
3246 KIRQL OldIrql;
3247
3248 SectionObject = (PSECTION)ObjectBody;
3249
3250 if (SectionObject->u.Flags.Based == 1)
3251 {
3252 /* Remove the node from the global section address tree */
3253 KeAcquireGuardedMutex(&MmSectionBasedMutex);
3254 MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot);
3255 KeReleaseGuardedMutex(&MmSectionBasedMutex);
3256 }
3257
3258 /* Lock the PFN database */
3259 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
3260
3261 ASSERT(SectionObject->Segment);
3262 ASSERT(SectionObject->Segment->ControlArea);
3263
3264 ControlArea = SectionObject->Segment->ControlArea;
3265
3266 /* Dereference */
3267 ControlArea->NumberOfSectionReferences--;
3268 ControlArea->NumberOfUserReferences--;
3269
3270 ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
3271
3272 /* Check it. It will delete it if there is no more reference to it */
3273 MiCheckControlArea(ControlArea, OldIrql);
3274 }
3275
3276 ULONG
3277 NTAPI
3278 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer)
3279 {
3280 UNIMPLEMENTED;
3281 return 0;
3282 }
3283
3284 /* SYSTEM CALLS ***************************************************************/
3285
3286 NTSTATUS
3287 NTAPI
3288 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3289 IN PVOID File2MappedAsFile)
3290 {
3291 PVOID AddressSpace;
3292 PMMVAD Vad1, Vad2;
3293 PFILE_OBJECT FileObject1, FileObject2;
3294 NTSTATUS Status;
3295
3296 /* Lock address space */
3297 AddressSpace = MmGetCurrentAddressSpace();
3298 MmLockAddressSpace(AddressSpace);
3299
3300 /* Get the VAD for Address 1 */
3301 Vad1 = MiLocateAddress(File1MappedAsAnImage);
3302 if (Vad1 == NULL)
3303 {
3304 /* Fail, the address does not exist */
3305 DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage);
3306 Status = STATUS_INVALID_ADDRESS;
3307 goto Exit;
3308 }
3309
3310 /* Get the VAD for Address 2 */
3311 Vad2 = MiLocateAddress(File2MappedAsFile);
3312 if (Vad2 == NULL)
3313 {
3314 /* Fail, the address does not exist */
3315 DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile);
3316 Status = STATUS_INVALID_ADDRESS;
3317 goto Exit;
3318 }
3319
3320 /* Get the file object pointer for VAD 1 */
3321 FileObject1 = MiGetFileObjectForVad(Vad1);
3322 if (FileObject1 == NULL)
3323 {
3324 DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage);
3325 Status = STATUS_CONFLICTING_ADDRESSES;
3326 goto Exit;