[NTOS:MM] Use inline functions to acquire/release the PFN lock.
[reactos.git] / 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 = MiAcquirePfnLock();
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 MiReleasePfnLock(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 = MiAcquirePfnLock();
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 MiReleasePfnLock(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 = MiAcquirePfnLock();
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 MiReleasePfnLock(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 MI_ASSERT_PFN_LOCK_HELD();
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 MiReleasePfnLock(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 = MiAcquirePfnLock();
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 = MiAcquirePfnLock();
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, Process->ProcessExiting);
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 = MiAcquirePfnLock();
997 MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
998 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
999 Color = (++MmSessionSpace->Color) & MmSecondaryColorMask;
1000 PageFrameNumber = MiRemoveZeroPage(Color);
1001 TempPde.u.Hard.PageFrameNumber = PageFrameNumber;
1002 MI_WRITE_VALID_PDE(StartPde, TempPde);
1003
1004 /* Write the page table in session space structure */
1005 ASSERT(MmSessionSpace->PageTables[Index].u.Long == 0);
1006 MmSessionSpace->PageTables[Index] = TempPde;
1007
1008 /* Initialize the PFN */
1009 MiInitializePfnForOtherProcess(PageFrameNumber,
1010 StartPde,
1011 MmSessionSpace->SessionPageDirectoryIndex);
1012
1013 /* And now release the lock */
1014 MiReleasePfnLock(OldIrql);
1015
1016 /* Get the PFN entry and make sure there's no event for it */
1017 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
1018 ASSERT(Pfn1->u1.Event == NULL);
1019
1020 /* Increment the number of pages */
1021 ActualPages++;
1022 }
1023
1024 /* Move to the next PDE */
1025 StartPde++;
1026 Index++;
1027 }
1028 #endif
1029
1030 /* Make sure we didn't do more pages than expected */
1031 ASSERT(ActualPages <= PageCount);
1032
1033 /* Release the working set lock */
1034 // MiUnlockWorkingSet(PsGetCurrentThread(),
1035 // &MmSessionSpace->GlobalVirtualAddress->Vm);
1036
1037
1038 /* If we did at least one page... */
1039 if (ActualPages)
1040 {
1041 /* Update the performance counters! */
1042 InterlockedExchangeAddSizeT(&MmSessionSpace->NonPageablePages, ActualPages);
1043 InterlockedExchangeAddSizeT(&MmSessionSpace->CommittedPages, ActualPages);
1044 }
1045
1046 /* Return status */
1047 return STATUS_SUCCESS;
1048 }
1049
1050 NTSTATUS
1051 NTAPI
1052 MiMapViewInSystemSpace(IN PVOID Section,
1053 IN PMMSESSION Session,
1054 OUT PVOID *MappedBase,
1055 IN OUT PSIZE_T ViewSize)
1056 {
1057 PVOID Base;
1058 PCONTROL_AREA ControlArea;
1059 ULONG Buckets, SectionSize;
1060 NTSTATUS Status;
1061 PAGED_CODE();
1062
1063 /* Get the control area, check for any flags ARM3 doesn't yet support */
1064 ControlArea = ((PSECTION)Section)->Segment->ControlArea;
1065 ASSERT(ControlArea->u.Flags.Image == 0);
1066 ASSERT(ControlArea->FilePointer == NULL);
1067 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1068 ASSERT(ControlArea->u.Flags.Rom == 0);
1069 ASSERT(ControlArea->u.Flags.WasPurged == 0);
1070
1071 /* Increase the reference and map count on the control area, no purges yet */
1072 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1073 ASSERT(NT_SUCCESS(Status));
1074
1075 /* Get the section size at creation time */
1076 SectionSize = ((PSECTION)Section)->SizeOfSection.LowPart;
1077
1078 /* If the caller didn't specify a view size, assume the whole section */
1079 if (!(*ViewSize)) *ViewSize = SectionSize;
1080
1081 /* Check if the caller wanted a larger section than the view */
1082 if (*ViewSize > SectionSize)
1083 {
1084 /* Fail */
1085 DPRINT1("View is too large\n");
1086 MiDereferenceControlArea(ControlArea);
1087 return STATUS_INVALID_VIEW_SIZE;
1088 }
1089
1090 /* Get the number of 64K buckets required for this mapping */
1091 Buckets = (ULONG)(*ViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE);
1092 if (*ViewSize & (MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) Buckets++;
1093
1094 /* Check if the view is more than 4GB large */
1095 if (Buckets >= MI_SYSTEM_VIEW_BUCKET_SIZE)
1096 {
1097 /* Fail */
1098 DPRINT1("View is too large\n");
1099 MiDereferenceControlArea(ControlArea);
1100 return STATUS_INVALID_VIEW_SIZE;
1101 }
1102
1103 /* Insert this view into system space and get a base address for it */
1104 Base = MiInsertInSystemSpace(Session, Buckets, ControlArea);
1105 if (!Base)
1106 {
1107 /* Fail */
1108 DPRINT1("Out of system space\n");
1109 MiDereferenceControlArea(ControlArea);
1110 return STATUS_NO_MEMORY;
1111 }
1112
1113 /* What's the underlying session? */
1114 if (Session == &MmSession)
1115 {
1116 /* Create the PDEs needed for this mapping, and double-map them if needed */
1117 MiFillSystemPageDirectory(Base, Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE);
1118 Status = STATUS_SUCCESS;
1119 }
1120 else
1121 {
1122 /* Create the PDEs needed for this mapping */
1123 Status = MiSessionCommitPageTables(Base,
1124 (PVOID)((ULONG_PTR)Base +
1125 Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE));
1126 ASSERT(NT_SUCCESS(Status));
1127 }
1128
1129 /* Create the actual prototype PTEs for this mapping */
1130 Status = MiAddMappedPtes(MiAddressToPte(Base),
1131 BYTES_TO_PAGES(*ViewSize),
1132 ControlArea);
1133 ASSERT(NT_SUCCESS(Status));
1134
1135 /* Return the base adress of the mapping and success */
1136 *MappedBase = Base;
1137 return STATUS_SUCCESS;
1138 }
1139
1140 VOID
1141 NTAPI
1142 MiSetControlAreaSymbolsLoaded(IN PCONTROL_AREA ControlArea)
1143 {
1144 KIRQL OldIrql;
1145
1146 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1147
1148 OldIrql = MiAcquirePfnLock();
1149 ControlArea->u.Flags.DebugSymbolsLoaded |= 1;
1150
1151 ASSERT(OldIrql <= APC_LEVEL);
1152 MiReleasePfnLock(OldIrql);
1153 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1154 }
1155
1156 VOID
1157 NTAPI
1158 MiLoadUserSymbols(IN PCONTROL_AREA ControlArea,
1159 IN PVOID BaseAddress,
1160 IN PEPROCESS Process)
1161 {
1162 NTSTATUS Status;
1163 ANSI_STRING FileNameA;
1164 PLIST_ENTRY NextEntry;
1165 PUNICODE_STRING FileName;
1166 PIMAGE_NT_HEADERS NtHeaders;
1167 PLDR_DATA_TABLE_ENTRY LdrEntry;
1168
1169 FileName = &ControlArea->FilePointer->FileName;
1170 if (FileName->Length == 0)
1171 {
1172 return;
1173 }
1174
1175 /* Acquire module list lock */
1176 KeEnterCriticalRegion();
1177 ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
1178
1179 /* Browse list to try to find current module */
1180 for (NextEntry = MmLoadedUserImageList.Flink;
1181 NextEntry != &MmLoadedUserImageList;
1182 NextEntry = NextEntry->Flink)
1183 {
1184 /* Get the entry */
1185 LdrEntry = CONTAINING_RECORD(NextEntry,
1186 LDR_DATA_TABLE_ENTRY,
1187 InLoadOrderLinks);
1188
1189 /* If already in the list, increase load count */
1190 if (LdrEntry->DllBase == BaseAddress)
1191 {
1192 ++LdrEntry->LoadCount;
1193 break;
1194 }
1195 }
1196
1197 /* Not in the list, we'll add it */
1198 if (NextEntry == &MmLoadedUserImageList)
1199 {
1200 /* Allocate our element, taking to the name string and its null char */
1201 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry), 'bDmM');
1202 if (LdrEntry)
1203 {
1204 memset(LdrEntry, 0, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry));
1205
1206 _SEH2_TRY
1207 {
1208 /* Get image checksum and size */
1209 NtHeaders = RtlImageNtHeader(BaseAddress);
1210 if (NtHeaders)
1211 {
1212 LdrEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
1213 LdrEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
1214 }
1215 }
1216 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1217 {
1218 ExFreePoolWithTag(LdrEntry, 'bDmM');
1219 _SEH2_YIELD(return);
1220 }
1221 _SEH2_END;
1222
1223 /* Fill all the details */
1224 LdrEntry->DllBase = BaseAddress;
1225 LdrEntry->FullDllName.Buffer = (PVOID)((ULONG_PTR)LdrEntry + sizeof(*LdrEntry));
1226 LdrEntry->FullDllName.Length = FileName->Length;
1227 LdrEntry->FullDllName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL);
1228 memcpy(LdrEntry->FullDllName.Buffer, FileName->Buffer, FileName->Length);
1229 LdrEntry->FullDllName.Buffer[LdrEntry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1230 LdrEntry->LoadCount = 1;
1231
1232 /* Insert! */
1233 InsertHeadList(&MmLoadedUserImageList, &LdrEntry->InLoadOrderLinks);
1234 }
1235 }
1236
1237 /* Release locks */
1238 ExReleaseResourceLite(&PsLoadedModuleResource);
1239 KeLeaveCriticalRegion();
1240
1241 /* Load symbols */
1242 Status = RtlUnicodeStringToAnsiString(&FileNameA, FileName, TRUE);
1243 if (NT_SUCCESS(Status))
1244 {
1245 DbgLoadImageSymbols(&FileNameA, BaseAddress, (ULONG_PTR)Process->UniqueProcessId);
1246 RtlFreeAnsiString(&FileNameA);
1247 }
1248 }
1249
1250 NTSTATUS
1251 NTAPI
1252 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
1253 IN PEPROCESS Process,
1254 IN PVOID *BaseAddress,
1255 IN PLARGE_INTEGER SectionOffset,
1256 IN PSIZE_T ViewSize,
1257 IN PSECTION Section,
1258 IN SECTION_INHERIT InheritDisposition,
1259 IN ULONG ProtectionMask,
1260 IN SIZE_T CommitSize,
1261 IN ULONG_PTR ZeroBits,
1262 IN ULONG AllocationType)
1263 {
1264 PMMVAD_LONG Vad;
1265 ULONG_PTR StartAddress;
1266 ULONG_PTR ViewSizeInPages;
1267 PSUBSECTION Subsection;
1268 PSEGMENT Segment;
1269 PFN_NUMBER PteOffset;
1270 NTSTATUS Status;
1271 ULONG QuotaCharge = 0, QuotaExcess = 0;
1272 PMMPTE PointerPte, LastPte;
1273 MMPTE TempPte;
1274 ULONG Granularity = MM_VIRTMEM_GRANULARITY;
1275
1276 DPRINT("Mapping ARM3 data section\n");
1277
1278 /* Get the segment for this section */
1279 Segment = ControlArea->Segment;
1280
1281 #ifdef _M_IX86
1282 /* ALlow being less restrictive on x86. */
1283 if (AllocationType & MEM_DOS_LIM)
1284 Granularity = PAGE_SIZE;
1285 #endif
1286
1287 /* One can only reserve a file-based mapping, not shared memory! */
1288 if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer))
1289 {
1290 return STATUS_INVALID_PARAMETER_9;
1291 }
1292
1293 /* First, increase the map count. No purging is supported yet */
1294 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1295 if (!NT_SUCCESS(Status)) return Status;
1296
1297 /* Check if the caller specified the view size */
1298 if (!(*ViewSize))
1299 {
1300 /* The caller did not, so pick a 64K aligned view size based on the offset */
1301 SectionOffset->LowPart &= ~(_64K - 1);
1302 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
1303 }
1304 else
1305 {
1306 /* A size was specified, align it to a 64K boundary */
1307 *ViewSize += SectionOffset->LowPart & (_64K - 1);
1308
1309 /* Align the offset as well to make this an aligned map */
1310 SectionOffset->LowPart &= ~((ULONG)_64K - 1);
1311 }
1312
1313 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */
1314 ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0);
1315
1316 /* It's illegal to try to map more than overflows a LONG_PTR */
1317 if (*ViewSize >= MAXLONG_PTR)
1318 {
1319 MiDereferenceControlArea(ControlArea);
1320 return STATUS_INVALID_VIEW_SIZE;
1321 }
1322
1323 /* Windows ASSERTs for this flag */
1324 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1325
1326 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
1327 ASSERT(ControlArea->u.Flags.Rom == 0);
1328 Subsection = (PSUBSECTION)(ControlArea + 1);
1329
1330 /* Sections with extended segments are not supported in ARM3 */
1331 ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0);
1332
1333 /* Within this section, figure out which PTEs will describe the view */
1334 PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT);
1335
1336 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */
1337 ASSERT(PteOffset < Segment->TotalNumberOfPtes);
1338 ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset);
1339
1340 /* In ARM3, only one subsection is used for now. It must contain these PTEs */
1341 ASSERT(PteOffset < Subsection->PtesInSubsection);
1342
1343 /* In ARM3, only page-file backed sections (shared memory) are supported now */
1344 ASSERT(ControlArea->FilePointer == NULL);
1345
1346 /* Windows ASSERTs for this too -- there must be a subsection base address */
1347 ASSERT(Subsection->SubsectionBase != NULL);
1348
1349 /* Compute how much commit space the segment will take */
1350 if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes))
1351 {
1352 /* Charge for the maximum pages */
1353 QuotaCharge = BYTES_TO_PAGES(CommitSize);
1354 }
1355
1356 /* ARM3 does not currently support large pages */
1357 ASSERT(Segment->SegmentFlags.LargePages == 0);
1358
1359 /* Calculate how many pages the region spans */
1360 ViewSizeInPages = BYTES_TO_PAGES(*ViewSize);
1361
1362 /* A VAD can now be allocated. Do so and zero it out */
1363 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */
1364 ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */
1365 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
1366 if (!Vad)
1367 {
1368 MiDereferenceControlArea(ControlArea);
1369 return STATUS_INSUFFICIENT_RESOURCES;
1370 }
1371
1372 RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
1373 Vad->u4.Banked = (PVOID)0xDEADBABE;
1374
1375 /* Write all the data required in the VAD for handling a fault */
1376 Vad->ControlArea = ControlArea;
1377 Vad->u.VadFlags.CommitCharge = 0;
1378 Vad->u.VadFlags.Protection = ProtectionMask;
1379 Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16);
1380 Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
1381 if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange))
1382 {
1383 /* This isn't really implemented yet, but handle setting the flag */
1384 Vad->u.VadFlags.NoChange = 1;
1385 Vad->u2.VadFlags2.SecNoChange = 1;
1386 }
1387
1388 /* Finally, write down the first and last prototype PTE */
1389 Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
1390 PteOffset += ViewSizeInPages - 1;
1391 ASSERT(PteOffset < Subsection->PtesInSubsection);
1392 Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset];
1393
1394 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */
1395 ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
1396
1397 /* FIXME: Should setup VAD bitmap */
1398 Status = STATUS_SUCCESS;
1399
1400 /* Check if anything was committed */
1401 if (QuotaCharge)
1402 {
1403 /* Set the start and end PTE addresses, and pick the template PTE */
1404 PointerPte = Vad->FirstPrototypePte;
1405 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
1406 TempPte = Segment->SegmentPteTemplate;
1407
1408 /* Acquire the commit lock and loop all prototype PTEs to be committed */
1409 KeAcquireGuardedMutex(&MmSectionCommitMutex);
1410 while (PointerPte < LastPte)
1411 {
1412 /* Make sure the PTE is already invalid */
1413 if (PointerPte->u.Long == 0)
1414 {
1415 /* And write the invalid PTE */
1416 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1417 }
1418 else
1419 {
1420 /* The PTE is valid, so skip it */
1421 QuotaExcess++;
1422 }
1423
1424 /* Move to the next PTE */
1425 PointerPte++;
1426 }
1427
1428 /* Now check how many pages exactly we committed, and update accounting */
1429 ASSERT(QuotaCharge >= QuotaExcess);
1430 QuotaCharge -= QuotaExcess;
1431 Segment->NumberOfCommittedPages += QuotaCharge;
1432 ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes);
1433
1434 /* Now that we're done, release the lock */
1435 KeReleaseGuardedMutex(&MmSectionCommitMutex);
1436 }
1437
1438 /* Is it SEC_BASED, or did the caller manually specify an address? */
1439 if (*BaseAddress != NULL)
1440 {
1441 /* Just align what the caller gave us */
1442 StartAddress = ALIGN_DOWN_BY((ULONG_PTR)*BaseAddress, Granularity);
1443 }
1444 else if (Section->Address.StartingVpn != 0)
1445 {
1446 /* It is a SEC_BASED mapping, use the address that was generated */
1447 StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart;
1448 }
1449 else
1450 {
1451 StartAddress = 0;
1452 }
1453
1454 /* Insert the VAD */
1455 Status = MiInsertVadEx((PMMVAD)Vad,
1456 &StartAddress,
1457 ViewSizeInPages * PAGE_SIZE,
1458 MAXULONG_PTR >> ZeroBits,
1459 Granularity,
1460 AllocationType);
1461 if (!NT_SUCCESS(Status))
1462 {
1463 return Status;
1464 }
1465
1466 /* Windows stores this for accounting purposes, do so as well */
1467 if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress;
1468
1469 /* Finally, let the caller know where, and for what size, the view was mapped */
1470 *ViewSize = ViewSizeInPages * PAGE_SIZE;
1471 *BaseAddress = (PVOID)StartAddress;
1472 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize);
1473 return STATUS_SUCCESS;
1474 }
1475
1476 VOID
1477 NTAPI
1478 MiSubsectionConsistent(IN PSUBSECTION Subsection)
1479 {
1480 /* ReactOS only supports systems with 4K pages and 4K sectors */
1481 ASSERT(Subsection->u.SubsectionFlags.SectorEndOffset == 0);
1482
1483 /* Therefore, then number of PTEs should be equal to the number of sectors */
1484 if (Subsection->NumberOfFullSectors != Subsection->PtesInSubsection)
1485 {
1486 /* Break and warn if this is inconsistent */
1487 DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n",
1488 Subsection->NumberOfFullSectors, Subsection->PtesInSubsection);
1489 DbgBreakPoint();
1490 }
1491 }
1492
1493 NTSTATUS
1494 NTAPI
1495 MiCreateDataFileMap(IN PFILE_OBJECT File,
1496 OUT PSEGMENT *Segment,
1497 IN PSIZE_T MaximumSize,
1498 IN ULONG SectionPageProtection,
1499 IN ULONG AllocationAttributes,
1500 IN ULONG IgnoreFileSizing)
1501 {
1502 /* Not yet implemented */
1503 ASSERT(FALSE);
1504 *Segment = NULL;
1505 return STATUS_NOT_IMPLEMENTED;
1506 }
1507
1508 NTSTATUS
1509 NTAPI
1510 MiCreatePagingFileMap(OUT PSEGMENT *Segment,
1511 IN PSIZE_T MaximumSize,
1512 IN ULONG ProtectionMask,
1513 IN ULONG AllocationAttributes)
1514 {
1515 SIZE_T SizeLimit;
1516 PFN_COUNT PteCount;
1517 PMMPTE PointerPte;
1518 MMPTE TempPte;
1519 PCONTROL_AREA ControlArea;
1520 PSEGMENT NewSegment;
1521 PSUBSECTION Subsection;
1522 PAGED_CODE();
1523
1524 /* No large pages in ARM3 yet */
1525 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
1526
1527 /* Pagefile-backed sections need a known size */
1528 if (!(*MaximumSize)) return STATUS_INVALID_PARAMETER_4;
1529
1530 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */
1531 SizeLimit = MAXULONG_PTR - sizeof(SEGMENT);
1532 SizeLimit /= sizeof(MMPTE);
1533 SizeLimit <<= PAGE_SHIFT;
1534
1535 /* Fail if this size is too big */
1536 if (*MaximumSize > SizeLimit) return STATUS_SECTION_TOO_BIG;
1537
1538 /* Calculate how many Prototype PTEs will be needed */
1539 PteCount = (PFN_COUNT)((*MaximumSize + PAGE_SIZE - 1) >> PAGE_SHIFT);
1540
1541 /* For commited memory, we must have a valid protection mask */
1542 if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0);
1543
1544 /* The segment contains all the Prototype PTEs, allocate it in paged pool */
1545 NewSegment = ExAllocatePoolWithTag(PagedPool,
1546 sizeof(SEGMENT) +
1547 sizeof(MMPTE) * (PteCount - 1),
1548 'tSmM');
1549 ASSERT(NewSegment);
1550 *Segment = NewSegment;
1551
1552 /* Now allocate the control area, which has the subsection structure */
1553 ControlArea = ExAllocatePoolWithTag(NonPagedPool,
1554 sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
1555 'tCmM');
1556 ASSERT(ControlArea);
1557
1558 /* And zero it out, filling the basic segmnet pointer and reference fields */
1559 RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
1560 ControlArea->Segment = NewSegment;
1561 ControlArea->NumberOfSectionReferences = 1;
1562 ControlArea->NumberOfUserReferences = 1;
1563
1564 /* Convert allocation attributes to control area flags */
1565 if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1;
1566 if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1;
1567 if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1;
1568
1569 /* We just allocated it */
1570 ControlArea->u.Flags.BeingCreated = 1;
1571
1572 /* The subsection follows, write the mask, PTE count and point back to the CA */
1573 Subsection = (PSUBSECTION)(ControlArea + 1);
1574 Subsection->ControlArea = ControlArea;
1575 Subsection->PtesInSubsection = PteCount;
1576 Subsection->u.SubsectionFlags.Protection = ProtectionMask;
1577
1578 /* Zero out the segment's prototype PTEs, and link it with the control area */
1579 PointerPte = &NewSegment->ThePtes[0];
1580 RtlZeroMemory(NewSegment, sizeof(SEGMENT));
1581 NewSegment->PrototypePte = PointerPte;
1582 NewSegment->ControlArea = ControlArea;
1583
1584 /* Save some extra accounting data for the segment as well */
1585 NewSegment->u1.CreatingProcess = PsGetCurrentProcess();
1586 NewSegment->SizeOfSegment = PteCount * PAGE_SIZE;
1587 NewSegment->TotalNumberOfPtes = PteCount;
1588 NewSegment->NonExtendedPtes = PteCount;
1589
1590 /* The subsection's base address is the first Prototype PTE in the segment */
1591 Subsection->SubsectionBase = PointerPte;
1592
1593 /* Start with an empty PTE, unless this is a commit operation */
1594 TempPte.u.Long = 0;
1595 if (AllocationAttributes & SEC_COMMIT)
1596 {
1597 /* In which case, write down the protection mask in the Prototype PTEs */
1598 TempPte.u.Soft.Protection = ProtectionMask;
1599
1600 /* For accounting, also mark these pages as being committed */
1601 NewSegment->NumberOfCommittedPages = PteCount;
1602 }
1603
1604 /* The template PTE itself for the segment should also have the mask set */
1605 NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
1606
1607 /* Write out the prototype PTEs, for now they're simply demand zero */
1608 #ifdef _WIN64
1609 RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1610 #else
1611 RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1612 #endif
1613 return STATUS_SUCCESS;
1614 }
1615
1616 NTSTATUS
1617 NTAPI
1618 MiGetFileObjectForSectionAddress(
1619 IN PVOID Address,
1620 OUT PFILE_OBJECT *FileObject)
1621 {
1622 PMMVAD Vad;
1623 PCONTROL_AREA ControlArea;
1624
1625 /* Get the VAD */
1626 Vad = MiLocateAddress(Address);
1627 if (Vad == NULL)
1628 {
1629 /* Fail, the address does not exist */
1630 DPRINT1("Invalid address\n");
1631 return STATUS_INVALID_ADDRESS;
1632 }
1633
1634 /* Check if this is a RosMm memory area */
1635 if (Vad->u.VadFlags.Spare != 0)
1636 {
1637 PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad;
1638 PROS_SECTION_OBJECT Section;
1639
1640 /* Check if it's a section view (RosMm section) */
1641 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1642 {
1643 /* Get the section pointer to the SECTION_OBJECT */
1644 Section = MemoryArea->Data.SectionData.Section;
1645 *FileObject = Section->FileObject;
1646 }
1647 else
1648 {
1649 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE);
1650 DPRINT1("Address is a cache section!\n");
1651 return STATUS_SECTION_NOT_IMAGE;
1652 }
1653 }
1654 else
1655 {
1656 /* Make sure it's not a VM VAD */
1657 if (Vad->u.VadFlags.PrivateMemory == 1)
1658 {
1659 DPRINT1("Address is not a section\n");
1660 return STATUS_SECTION_NOT_IMAGE;
1661 }
1662
1663 /* Get the control area */
1664 ControlArea = Vad->ControlArea;
1665 if (!(ControlArea) || !(ControlArea->u.Flags.Image))
1666 {
1667 DPRINT1("Address is not a section\n");
1668 return STATUS_SECTION_NOT_IMAGE;
1669 }
1670
1671 /* Get the file object */
1672 *FileObject = ControlArea->FilePointer;
1673 }
1674
1675 /* Return success */
1676 return STATUS_SUCCESS;
1677 }
1678
1679 PFILE_OBJECT
1680 NTAPI
1681 MmGetFileObjectForSection(IN PVOID SectionObject)
1682 {
1683 PSECTION_OBJECT Section;
1684 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1685 ASSERT(SectionObject != NULL);
1686
1687 /* Check if it's an ARM3, or ReactOS section */
1688 if (MiIsRosSectionObject(SectionObject) == FALSE)
1689 {
1690 /* Return the file pointer stored in the control area */
1691 Section = SectionObject;
1692 return Section->Segment->ControlArea->FilePointer;
1693 }
1694
1695 /* Return the file object */
1696 return ((PROS_SECTION_OBJECT)SectionObject)->FileObject;
1697 }
1698
1699 static
1700 PFILE_OBJECT
1701 MiGetFileObjectForVad(
1702 _In_ PMMVAD Vad)
1703 {
1704 PCONTROL_AREA ControlArea;
1705 PFILE_OBJECT FileObject;
1706
1707 /* Check if this is a RosMm memory area */
1708 if (Vad->u.VadFlags.Spare != 0)
1709 {
1710 PMEMORY_AREA MemoryArea = (PMEMORY_AREA)Vad;
1711 PROS_SECTION_OBJECT Section;
1712
1713 /* Check if it's a section view (RosMm section) */
1714 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1715 {
1716 /* Get the section pointer to the SECTION_OBJECT */
1717 Section = MemoryArea->Data.SectionData.Section;
1718 FileObject = Section->FileObject;
1719 }
1720 else
1721 {
1722 ASSERT(MemoryArea->Type == MEMORY_AREA_CACHE);
1723 DPRINT1("VAD is a cache section!\n");
1724 return NULL;
1725 }
1726 }
1727 else
1728 {
1729 /* Make sure it's not a VM VAD */
1730 if (Vad->u.VadFlags.PrivateMemory == 1)
1731 {
1732 DPRINT1("VAD is not a section\n");
1733 return NULL;
1734 }
1735
1736 /* Get the control area */
1737 ControlArea = Vad->ControlArea;
1738 if ((ControlArea == NULL) || !ControlArea->u.Flags.Image)
1739 {
1740 DPRINT1("Address is not a section\n");
1741 return NULL;
1742 }
1743
1744 /* Get the file object */
1745 FileObject = ControlArea->FilePointer;
1746 }
1747
1748 /* Return the file object */
1749 return FileObject;
1750 }
1751
1752 VOID
1753 NTAPI
1754 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation)
1755 {
1756 PSECTION_OBJECT SectionObject;
1757
1758 /* Get the section object of this process*/
1759 SectionObject = PsGetCurrentProcess()->SectionObject;
1760 ASSERT(SectionObject != NULL);
1761 ASSERT(MiIsRosSectionObject(SectionObject) == TRUE);
1762
1763 /* Return the image information */
1764 *ImageInformation = ((PROS_SECTION_OBJECT)SectionObject)->ImageSection->ImageInformation;
1765 }
1766
1767 NTSTATUS
1768 NTAPI
1769 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject,
1770 OUT POBJECT_NAME_INFORMATION *ModuleName)
1771 {
1772 POBJECT_NAME_INFORMATION ObjectNameInfo;
1773 NTSTATUS Status;
1774 ULONG ReturnLength;
1775
1776 /* Allocate memory for our structure */
1777 ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, TAG_MM);
1778 if (!ObjectNameInfo) return STATUS_NO_MEMORY;
1779
1780 /* Query the name */
1781 Status = ObQueryNameString(FileObject,
1782 ObjectNameInfo,
1783 1024,
1784 &ReturnLength);
1785 if (!NT_SUCCESS(Status))
1786 {
1787 /* Failed, free memory */
1788 DPRINT1("Name query failed\n");
1789 ExFreePoolWithTag(ObjectNameInfo, TAG_MM);
1790 *ModuleName = NULL;
1791 return Status;
1792 }
1793
1794 /* Success */
1795 *ModuleName = ObjectNameInfo;
1796 return STATUS_SUCCESS;
1797 }
1798
1799 NTSTATUS
1800 NTAPI
1801 MmGetFileNameForSection(IN PVOID Section,
1802 OUT POBJECT_NAME_INFORMATION *ModuleName)
1803 {
1804 PFILE_OBJECT FileObject;
1805
1806 /* Make sure it's an image section */
1807 if (MiIsRosSectionObject(Section) == FALSE)
1808 {
1809 /* Check ARM3 Section flag */
1810 if (((PSECTION)Section)->u.Flags.Image == 0)
1811 {
1812 /* It's not, fail */
1813 DPRINT1("Not an image section\n");
1814 return STATUS_SECTION_NOT_IMAGE;
1815 }
1816 }
1817 else if (!(((PROS_SECTION_OBJECT)Section)->AllocationAttributes & SEC_IMAGE))
1818 {
1819 /* It's not, fail */
1820 DPRINT1("Not an image section\n");
1821 return STATUS_SECTION_NOT_IMAGE;
1822 }
1823
1824 /* Get the file object */
1825 FileObject = MmGetFileObjectForSection(Section);
1826 return MmGetFileNameForFileObject(FileObject, ModuleName);
1827 }
1828
1829 NTSTATUS
1830 NTAPI
1831 MmGetFileNameForAddress(IN PVOID Address,
1832 OUT PUNICODE_STRING ModuleName)
1833 {
1834 POBJECT_NAME_INFORMATION ModuleNameInformation;
1835 PVOID AddressSpace;
1836 NTSTATUS Status;
1837 PMMVAD Vad;
1838 PFILE_OBJECT FileObject = NULL;
1839
1840 /* Lock address space */
1841 AddressSpace = MmGetCurrentAddressSpace();
1842 MmLockAddressSpace(AddressSpace);
1843
1844 /* Get the VAD */
1845 Vad = MiLocateAddress(Address);
1846 if (Vad == NULL)
1847 {
1848 /* Fail, the address does not exist */
1849 DPRINT1("No VAD at address %p\n", Address);
1850 MmUnlockAddressSpace(AddressSpace);
1851 return STATUS_INVALID_ADDRESS;
1852 }
1853
1854 /* Get the file object pointer for the VAD */
1855 FileObject = MiGetFileObjectForVad(Vad);
1856 if (FileObject == NULL)
1857 {
1858 DPRINT1("Failed to get file object for Address %p\n", Address);
1859 MmUnlockAddressSpace(AddressSpace);
1860 return STATUS_SECTION_NOT_IMAGE;
1861 }
1862
1863 /* Reference the file object */
1864 ObReferenceObject(FileObject);
1865
1866 /* Unlock address space */
1867 MmUnlockAddressSpace(AddressSpace);
1868
1869 /* Get the filename of the file object */
1870 Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation);
1871
1872 /* Dereference the file object */
1873 ObDereferenceObject(FileObject);
1874
1875 /* Check if we were able to get the file object name */
1876 if (NT_SUCCESS(Status))
1877 {
1878 /* Init modulename */
1879 RtlCreateUnicodeString(ModuleName, ModuleNameInformation->Name.Buffer);
1880
1881 /* Free temp taged buffer from MmGetFileNameForFileObject() */
1882 ExFreePoolWithTag(ModuleNameInformation, TAG_MM);
1883 DPRINT("Found ModuleName %S by address %p\n", ModuleName->Buffer, Address);
1884 }
1885
1886 /* Return status */
1887 return Status;
1888 }
1889
1890 NTSTATUS
1891 NTAPI
1892 MiQueryMemorySectionName(IN HANDLE ProcessHandle,
1893 IN PVOID BaseAddress,
1894 OUT PVOID MemoryInformation,
1895 IN SIZE_T MemoryInformationLength,
1896 OUT PSIZE_T ReturnLength)
1897 {
1898 PEPROCESS Process;
1899 NTSTATUS Status;
1900 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1901 UNICODE_STRING ModuleFileName;
1902 PMEMORY_SECTION_NAME SectionName = NULL;
1903 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1904
1905 Status = ObReferenceObjectByHandle(ProcessHandle,
1906 PROCESS_QUERY_INFORMATION,
1907 NULL,
1908 PreviousMode,
1909 (PVOID*)(&Process),
1910 NULL);
1911
1912 if (!NT_SUCCESS(Status))
1913 {
1914 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
1915 return Status;
1916 }
1917
1918 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1919 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
1920
1921 if (NT_SUCCESS(Status))
1922 {
1923 SectionName = MemoryInformation;
1924 if (PreviousMode != KernelMode)
1925 {
1926 _SEH2_TRY
1927 {
1928 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1929 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1930 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1931
1932 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1933
1934 }
1935 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1936 {
1937 Status = _SEH2_GetExceptionCode();
1938 }
1939 _SEH2_END;
1940 }
1941 else
1942 {
1943 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1944 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1945 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1946
1947 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1948
1949 }
1950 }
1951 ObDereferenceObject(Process);
1952 return Status;
1953 }
1954
1955 VOID
1956 NTAPI
1957 MiFlushTbAndCapture(IN PMMVAD FoundVad,
1958 IN PMMPTE PointerPte,
1959 IN ULONG ProtectionMask,
1960 IN PMMPFN Pfn1,
1961 IN BOOLEAN UpdateDirty)
1962 {
1963 MMPTE TempPte, PreviousPte;
1964 KIRQL OldIrql;
1965 BOOLEAN RebuildPte = FALSE;
1966
1967 //
1968 // User for sanity checking later on
1969 //
1970 PreviousPte = *PointerPte;
1971
1972 //
1973 // Build the PTE and acquire the PFN lock
1974 //
1975 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1976 PointerPte,
1977 ProtectionMask,
1978 PreviousPte.u.Hard.PageFrameNumber);
1979 OldIrql = MiAcquirePfnLock();
1980
1981 //
1982 // We don't support I/O mappings in this path yet
1983 //
1984 ASSERT(Pfn1 != NULL);
1985 ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined);
1986
1987 //
1988 // Make sure new protection mask doesn't get in conflict and fix it if it does
1989 //
1990 if (Pfn1->u3.e1.CacheAttribute == MiCached)
1991 {
1992 //
1993 // This is a cached PFN
1994 //
1995 if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS))
1996 {
1997 RebuildPte = TRUE;
1998 ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS);
1999 }
2000 }
2001 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
2002 {
2003 //
2004 // This is a non-cached PFN
2005 //
2006 if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE)
2007 {
2008 RebuildPte = TRUE;
2009 ProtectionMask &= ~MM_NOACCESS;
2010 ProtectionMask |= MM_NOCACHE;
2011 }
2012 }
2013
2014 if (RebuildPte)
2015 {
2016 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
2017 PointerPte,
2018 ProtectionMask,
2019 PreviousPte.u.Hard.PageFrameNumber);
2020 }
2021
2022 //
2023 // Write the new PTE, making sure we are only changing the bits
2024 //
2025 MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2026
2027 //
2028 // Flush the TLB
2029 //
2030 ASSERT(PreviousPte.u.Hard.Valid == 1);
2031 KeFlushCurrentTb();
2032 ASSERT(PreviousPte.u.Hard.Valid == 1);
2033
2034 //
2035 // Windows updates the relevant PFN1 information, we currently don't.
2036 //
2037 if (UpdateDirty && PreviousPte.u.Hard.Dirty)
2038 {
2039 if (!Pfn1->u3.e1.Modified)
2040 {
2041 DPRINT1("FIXME: Mark PFN as dirty\n");
2042 }
2043 }
2044
2045 //
2046 // Not supported in ARM3
2047 //
2048 ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch);
2049
2050 //
2051 // Release the PFN lock, we are done
2052 //
2053 MiReleasePfnLock(OldIrql);
2054 }
2055
2056 //
2057 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
2058 //
2059 NTSTATUS
2060 NTAPI
2061 MiSetProtectionOnSection(IN PEPROCESS Process,
2062 IN PMMVAD FoundVad,
2063 IN PVOID StartingAddress,
2064 IN PVOID EndingAddress,
2065 IN ULONG NewProtect,
2066 OUT PULONG CapturedOldProtect,
2067 IN ULONG DontCharge,
2068 OUT PULONG Locked)
2069 {
2070 PMMPTE PointerPte, LastPte;
2071 MMPTE TempPte, PteContents;
2072 PMMPDE PointerPde;
2073 PMMPFN Pfn1;
2074 ULONG ProtectionMask, QuotaCharge = 0;
2075 PETHREAD Thread = PsGetCurrentThread();
2076 PAGED_CODE();
2077
2078 //
2079 // Tell caller nothing is being locked
2080 //
2081 *Locked = FALSE;
2082
2083 //
2084 // This function should only be used for section VADs. Windows ASSERT */
2085 //
2086 ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0);
2087
2088 //
2089 // We don't support these features in ARM3
2090 //
2091 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2092 ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0);
2093
2094 //
2095 // Convert and validate the protection mask
2096 //
2097 ProtectionMask = MiMakeProtectionMask(NewProtect);
2098 if (ProtectionMask == MM_INVALID_PROTECTION)
2099 {
2100 DPRINT1("Invalid section protect\n");
2101 return STATUS_INVALID_PAGE_PROTECTION;
2102 }
2103
2104 //
2105 // Get the PTE and PDE for the address, as well as the final PTE
2106 //
2107 MiLockProcessWorkingSetUnsafe(Process, Thread);
2108 PointerPde = MiAddressToPde(StartingAddress);
2109 PointerPte = MiAddressToPte(StartingAddress);
2110 LastPte = MiAddressToPte(EndingAddress);
2111
2112 //
2113 // Make the PDE valid, and check the status of the first PTE
2114 //
2115 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2116 if (PointerPte->u.Long)
2117 {
2118 //
2119 // Not supported in ARM3
2120 //
2121 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
2122
2123 //
2124 // Capture the page protection and make the PDE valid
2125 //
2126 *CapturedOldProtect = MiGetPageProtection(PointerPte);
2127 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2128 }
2129 else
2130 {
2131 //
2132 // Only pagefile-backed section VADs are supported for now
2133 //
2134 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2135
2136 //
2137 // Grab the old protection from the VAD itself
2138 //
2139 *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection];
2140 }
2141
2142 //
2143 // Loop all the PTEs now
2144 //
2145 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2146 while (PointerPte <= LastPte)
2147 {
2148 //
2149 // Check if we've crossed a PDE boundary and make the new PDE valid too
2150 //
2151 if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
2152 {
2153 PointerPde = MiPteToPde(PointerPte);
2154 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2155 }
2156
2157 //
2158 // Capture the PTE and see what we're dealing with
2159 //
2160 PteContents = *PointerPte;
2161 if (PteContents.u.Long == 0)
2162 {
2163 //
2164 // This used to be a zero PTE and it no longer is, so we must add a
2165 // reference to the pagetable.
2166 //
2167 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2168
2169 //
2170 // Create the demand-zero prototype PTE
2171 //
2172 TempPte = PrototypePte;
2173 TempPte.u.Soft.Protection = ProtectionMask;
2174 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2175 }
2176 else if (PteContents.u.Hard.Valid == 1)
2177 {
2178 //
2179 // Get the PFN entry
2180 //
2181 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2182
2183 //
2184 // We don't support these yet
2185 //
2186 ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0);
2187 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2188
2189 //
2190 // Write the protection mask and write it with a TLB flush
2191 //
2192 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2193 MiFlushTbAndCapture(FoundVad,
2194 PointerPte,
2195 ProtectionMask,
2196 Pfn1,
2197 TRUE);
2198 }
2199 else
2200 {
2201 //
2202 // We don't support these cases yet
2203 //
2204 ASSERT(PteContents.u.Soft.Prototype == 0);
2205 ASSERT(PteContents.u.Soft.Transition == 0);
2206
2207 //
2208 // The PTE is already demand-zero, just update the protection mask
2209 //
2210 PointerPte->u.Soft.Protection = ProtectionMask;
2211 }
2212
2213 PointerPte++;
2214 }
2215
2216 //
2217 // Unlock the working set and update quota charges if needed, then return
2218 //
2219 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2220 if ((QuotaCharge > 0) && (!DontCharge))
2221 {
2222 FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
2223 Process->CommitCharge -= QuotaCharge;
2224 }
2225 return STATUS_SUCCESS;
2226 }
2227
2228 VOID
2229 NTAPI
2230 MiRemoveMappedPtes(IN PVOID BaseAddress,
2231 IN ULONG NumberOfPtes,
2232 IN PCONTROL_AREA ControlArea,
2233 IN PMMSUPPORT Ws)
2234 {
2235 PMMPTE PointerPte, ProtoPte;//, FirstPte;
2236 PMMPDE PointerPde, SystemMapPde;
2237 PMMPFN Pfn1, Pfn2;
2238 MMPTE PteContents;
2239 KIRQL OldIrql;
2240 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress);
2241
2242 ASSERT(Ws == NULL);
2243
2244 /* Get the PTE and loop each one */
2245 PointerPte = MiAddressToPte(BaseAddress);
2246 //FirstPte = PointerPte;
2247 while (NumberOfPtes)
2248 {
2249 /* Check if the PTE is already valid */
2250 PteContents = *PointerPte;
2251 if (PteContents.u.Hard.Valid == 1)
2252 {
2253 /* Get the PFN entry */
2254 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2255
2256 /* Get the PTE */
2257 PointerPde = MiPteToPde(PointerPte);
2258
2259 /* Lock the PFN database and make sure this isn't a mapped file */
2260 OldIrql = MiAcquirePfnLock();
2261 ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0);
2262
2263 /* Mark the page as modified accordingly */
2264 if (MI_IS_PAGE_DIRTY(&PteContents))
2265 Pfn1->u3.e1.Modified = 1;
2266
2267 /* Was the PDE invalid */
2268 if (PointerPde->u.Long == 0)
2269 {
2270 #if (_MI_PAGING_LEVELS == 2)
2271 /* Find the system double-mapped PDE that describes this mapping */
2272 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
2273
2274 /* Make it valid */
2275 ASSERT(SystemMapPde->u.Hard.Valid == 1);
2276 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde);
2277 #else
2278 DBG_UNREFERENCED_LOCAL_VARIABLE(SystemMapPde);
2279 ASSERT(FALSE);
2280 #endif
2281 }
2282
2283 /* Dereference the PDE and the PTE */
2284 Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
2285 MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2286 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
2287 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2288
2289 /* Release the PFN lock */
2290 MiReleasePfnLock(OldIrql);
2291 }
2292 else
2293 {
2294 /* Windows ASSERT */
2295 ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1));
2296
2297 /* Check if this is a prototype pointer PTE */
2298 if (PteContents.u.Soft.Prototype == 1)
2299 {
2300 /* Get the prototype PTE */
2301 ProtoPte = MiProtoPteToPte(&PteContents);
2302
2303 /* We don't support anything else atm */
2304 ASSERT(ProtoPte->u.Long == 0);
2305 }
2306 }
2307
2308 /* Make the PTE into a zero PTE */
2309 PointerPte->u.Long = 0;
2310
2311 /* Move to the next PTE */
2312 PointerPte++;
2313 NumberOfPtes--;
2314 }
2315
2316 /* Flush the TLB */
2317 KeFlushCurrentTb();
2318
2319 /* Acquire the PFN lock */
2320 OldIrql = MiAcquirePfnLock();
2321
2322 /* Decrement the accounting counters */
2323 ControlArea->NumberOfUserReferences--;
2324 ControlArea->NumberOfMappedViews--;
2325
2326 /* Check if we should destroy the CA and release the lock */
2327 MiCheckControlArea(ControlArea, OldIrql);
2328 }
2329
2330 ULONG
2331 NTAPI
2332 MiRemoveFromSystemSpace(IN PMMSESSION Session,
2333 IN PVOID Base,
2334 OUT PCONTROL_AREA *ControlArea)
2335 {
2336 ULONG Hash, Size, Count = 0;
2337 ULONG_PTR Entry;
2338 PAGED_CODE();
2339
2340 /* Compute the hash for this entry and loop trying to find it */
2341 Entry = (ULONG_PTR)Base >> 16;
2342 Hash = Entry % Session->SystemSpaceHashKey;
2343 while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry)
2344 {
2345 /* Check if we overflew past the end of the hash table */
2346 if (++Hash >= Session->SystemSpaceHashSize)
2347 {
2348 /* Reset the hash to zero and keep searching from the bottom */
2349 Hash = 0;
2350 if (++Count == 2)
2351 {
2352 /* But if we overflew twice, then this is not a real mapping */
2353 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2354 (ULONG_PTR)Base,
2355 1,
2356 0,
2357 0);
2358 }
2359 }
2360 }
2361
2362 /* One less entry */
2363 Session->SystemSpaceHashEntries--;
2364
2365 /* Extract the size and clear the entry */
2366 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2367 Session->SystemSpaceViewTable[Hash].Entry = 0;
2368
2369 /* Return the control area and the size */
2370 *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2371 return Size;
2372 }
2373
2374 NTSTATUS
2375 NTAPI
2376 MiUnmapViewInSystemSpace(IN PMMSESSION Session,
2377 IN PVOID MappedBase)
2378 {
2379 ULONG Size;
2380 PCONTROL_AREA ControlArea;
2381 PAGED_CODE();
2382
2383 /* Remove this mapping */
2384 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2385 Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
2386
2387 /* Clear the bits for this mapping */
2388 RtlClearBits(Session->SystemSpaceBitMap,
2389 (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16),
2390 Size);
2391
2392 /* Convert the size from a bit size into the actual size */
2393 Size = Size * (_64K >> PAGE_SHIFT);
2394
2395 /* Remove the PTEs now */
2396 MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL);
2397 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2398
2399 /* Return success */
2400 return STATUS_SUCCESS;
2401 }
2402
2403 /* PUBLIC FUNCTIONS ***********************************************************/
2404
2405 /*
2406 * @implemented
2407 */
2408 NTSTATUS
2409 NTAPI
2410 MmCreateArm3Section(OUT PVOID *SectionObject,
2411 IN ACCESS_MASK DesiredAccess,
2412 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2413 IN PLARGE_INTEGER InputMaximumSize,
2414 IN ULONG SectionPageProtection,
2415 IN ULONG AllocationAttributes,
2416 IN HANDLE FileHandle OPTIONAL,
2417 IN PFILE_OBJECT FileObject OPTIONAL)
2418 {
2419 SECTION Section;
2420 PSECTION NewSection;
2421 PSUBSECTION Subsection;
2422 PSEGMENT NewSegment, Segment;
2423 NTSTATUS Status;
2424 PCONTROL_AREA ControlArea;
2425 ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge;
2426 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2427 BOOLEAN FileLock = FALSE, KernelCall = FALSE;
2428 KIRQL OldIrql;
2429 PFILE_OBJECT File;
2430 BOOLEAN UserRefIncremented = FALSE;
2431 PVOID PreviousSectionPointer;
2432
2433 /* Make the same sanity checks that the Nt interface should've validated */
2434 ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
2435 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
2436 SEC_NO_CHANGE)) == 0);
2437 ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
2438 ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
2439 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
2440 SEC_NOCACHE | SEC_NO_CHANGE))));
2441 ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
2442 ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
2443 (SectionPageProtection & PAGE_WRITECOMBINE) ||
2444 (SectionPageProtection & PAGE_GUARD) ||
2445 (SectionPageProtection & PAGE_NOACCESS)));
2446
2447 /* Convert section flag to page flag */
2448 if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
2449
2450 /* Check to make sure the protection is correct. Nt* does this already */
2451 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
2452 if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
2453
2454 /* Check if this is going to be a data or image backed file section */
2455 if ((FileHandle) || (FileObject))
2456 {
2457 /* These cannot be mapped with large pages */
2458 if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6;
2459
2460 /* For now, only support the mechanism through a file handle */
2461 ASSERT(FileObject == NULL);
2462
2463 /* Reference the file handle to get the object */
2464 Status = ObReferenceObjectByHandle(FileHandle,
2465 MmMakeFileAccess[ProtectionMask],
2466 IoFileObjectType,
2467 PreviousMode,
2468 (PVOID*)&File,
2469 NULL);
2470 if (!NT_SUCCESS(Status)) return Status;
2471
2472 /* Make sure Cc has been doing its job */
2473 if (!File->SectionObjectPointer)
2474 {
2475 /* This is not a valid file system-based file, fail */
2476 ObDereferenceObject(File);
2477 return STATUS_INVALID_FILE_FOR_SECTION;
2478 }
2479
2480 /* Image-file backed sections are not yet supported */
2481 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2482
2483 /* Compute the size of the control area, and allocate it */
2484 ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
2485 ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM');
2486 if (!ControlArea)
2487 {
2488 ObDereferenceObject(File);
2489 return STATUS_INSUFFICIENT_RESOURCES;
2490 }
2491
2492 /* Zero it out */
2493 RtlZeroMemory(ControlArea, ControlAreaSize);
2494
2495 /* Did we get a handle, or an object? */
2496 if (FileHandle)
2497 {
2498 /* We got a file handle so we have to lock down the file */
2499 #if 0
2500 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection);
2501 if (!NT_SUCCESS(Status))
2502 {
2503 ExFreePool(ControlArea);
2504 ObDereferenceObject(File);
2505 return Status;
2506 }
2507 #else
2508 /* ReactOS doesn't support this API yet, so do nothing */
2509 Status = STATUS_SUCCESS;
2510 #endif
2511 /* Update the top-level IRP so that drivers know what's happening */
2512 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
2513 FileLock = TRUE;
2514 }
2515
2516 /* Lock the PFN database while we play with the section pointers */
2517 OldIrql = MiAcquirePfnLock();
2518
2519 /* Image-file backed sections are not yet supported */
2520 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2521
2522 /* There should not already be a control area for this file */
2523 ASSERT(File->SectionObjectPointer->DataSectionObject == NULL);
2524 NewSegment = NULL;
2525
2526 /* Write down that this CA is being created, and set it */
2527 ControlArea->u.Flags.BeingCreated = TRUE;
2528 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2529 PreviousSectionPointer = File->SectionObjectPointer;
2530 File->SectionObjectPointer->DataSectionObject = ControlArea;
2531
2532 /* We can release the PFN lock now */
2533 MiReleasePfnLock(OldIrql);
2534
2535 /* We don't support previously-mapped file */
2536 ASSERT(NewSegment == NULL);
2537
2538 /* Image-file backed sections are not yet supported */
2539 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2540
2541 /* So we always create a data file map */
2542 Status = MiCreateDataFileMap(File,
2543 &Segment,
2544 (PSIZE_T)InputMaximumSize,
2545 SectionPageProtection,
2546 AllocationAttributes,
2547 KernelCall);
2548 if (!NT_SUCCESS(Status))
2549 {
2550 /* Lock the PFN database while we play with the section pointers */
2551 OldIrql = MiAcquirePfnLock();
2552
2553 /* Reset the waiting-for-deletion event */
2554 ASSERT(ControlArea->WaitingForDeletion == NULL);
2555 ControlArea->WaitingForDeletion = NULL;
2556
2557 /* Set the file pointer NULL flag */
2558 ASSERT(ControlArea->u.Flags.FilePointerNull == 0);
2559 ControlArea->u.Flags.FilePointerNull = TRUE;
2560
2561 /* Delete the data section object */
2562 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2563 File->SectionObjectPointer->DataSectionObject = NULL;
2564
2565 /* No longer being created */
2566 ControlArea->u.Flags.BeingCreated = FALSE;
2567
2568 /* We can release the PFN lock now */
2569 MiReleasePfnLock(OldIrql);
2570
2571 /* Check if we locked and set the IRP */
2572 if (FileLock)
2573 {
2574 /* Undo */
2575 IoSetTopLevelIrp(NULL);
2576 //FsRtlReleaseFile(File);
2577 }
2578
2579 /* Free the control area and de-ref the file object */
2580 ExFreePool(ControlArea);
2581 ObDereferenceObject(File);
2582
2583 /* All done */
2584 return Status;
2585 }
2586
2587 /* On success, we expect this */
2588 ASSERT(PreviousSectionPointer == File->SectionObjectPointer);
2589
2590 /* Check if a maximum size was specified */
2591 if (!InputMaximumSize->QuadPart)
2592 {
2593 /* Nope, use the segment size */
2594 Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
2595 }
2596 else
2597 {
2598 /* Yep, use the entered size */
2599 Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart;
2600 }
2601 }
2602 else
2603 {
2604 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2605 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
2606
2607 /* Not yet supported */
2608 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
2609
2610 /* So this must be a pagefile-backed section, create the mappings needed */
2611 Status = MiCreatePagingFileMap(&NewSegment,
2612 (PSIZE_T)InputMaximumSize,
2613 ProtectionMask,
2614 AllocationAttributes);
2615 if (!NT_SUCCESS(Status)) return Status;
2616
2617 /* Set the size here, and read the control area */
2618 Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
2619 ControlArea = NewSegment->ControlArea;
2620
2621 /* MiCreatePagingFileMap increments user references */
2622 UserRefIncremented = TRUE;
2623 }
2624
2625 /* Did we already have a segment? */
2626 if (!NewSegment)
2627 {
2628 /* This must be the file path and we created a segment */
2629 NewSegment = Segment;
2630 ASSERT(File != NULL);
2631
2632 /* Acquire the PFN lock while we set control area flags */
2633 OldIrql = MiAcquirePfnLock();
2634
2635 /* We don't support this race condition yet, so assume no waiters */
2636 ASSERT(ControlArea->WaitingForDeletion == NULL);
2637 ControlArea->WaitingForDeletion = NULL;
2638
2639 /* Image-file backed sections are not yet supported, nor ROM images */
2640 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2641 ASSERT(Segment->ControlArea->u.Flags.Rom == 0);
2642
2643 /* Take off the being created flag, and then release the lock */
2644 ControlArea->u.Flags.BeingCreated = FALSE;
2645 MiReleasePfnLock(OldIrql);
2646 }
2647
2648 /* Check if we locked the file earlier */
2649 if (FileLock)
2650 {
2651 /* Reset the top-level IRP and release the lock */
2652 IoSetTopLevelIrp(NULL);
2653 //FsRtlReleaseFile(File);
2654 FileLock = FALSE;
2655 }
2656
2657 /* Set the initial section object data */
2658 Section.InitialPageProtection = SectionPageProtection;
2659
2660 /* The mapping created a control area and segment, save the flags */
2661 Section.Segment = NewSegment;
2662 Section.u.LongFlags = ControlArea->u.LongFlags;
2663
2664 /* Check if this is a user-mode read-write non-image file mapping */
2665 if (!(FileObject) &&
2666 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2667 !(ControlArea->u.Flags.Image) &&
2668 (ControlArea->FilePointer))
2669 {
2670 /* Add a reference and set the flag */
2671 Section.u.Flags.UserWritable = TRUE;
2672 InterlockedIncrement((volatile LONG*)&ControlArea->WritableUserReferences);
2673 }
2674
2675 /* Check for image mappings or page file mappings */
2676 if ((ControlArea->u.Flags.Image) || !(ControlArea->FilePointer))
2677 {
2678 /* Charge the segment size, and allocate a subsection */
2679 PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
2680 Size = sizeof(SUBSECTION);
2681 }
2682 else
2683 {
2684 /* Charge nothing, and allocate a mapped subsection */
2685 PagedCharge = 0;
2686 Size = sizeof(MSUBSECTION);
2687 }
2688
2689 /* Check if this is a normal CA */
2690 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2691 ASSERT(ControlArea->u.Flags.Rom == 0);
2692
2693 /* Charge only a CA, and the subsection is right after */
2694 NonPagedCharge = sizeof(CONTROL_AREA);
2695 Subsection = (PSUBSECTION)(ControlArea + 1);
2696
2697 /* We only support single-subsection mappings */
2698 NonPagedCharge += Size;
2699 ASSERT(Subsection->NextSubsection == NULL);
2700
2701 /* Create the actual section object, with enough space for the prototype PTEs */
2702 Status = ObCreateObject(PreviousMode,
2703 MmSectionObjectType,
2704 ObjectAttributes,
2705 PreviousMode,
2706 NULL,
2707 sizeof(SECTION),
2708 PagedCharge,
2709 NonPagedCharge,
2710 (PVOID*)&NewSection);
2711 if (!NT_SUCCESS(Status))
2712 {
2713 /* Check if this is a user-mode read-write non-image file mapping */
2714 if (!(FileObject) &&
2715 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2716 !(ControlArea->u.Flags.Image) &&
2717 (ControlArea->FilePointer))
2718 {
2719 /* Remove a reference and check the flag */
2720 ASSERT(Section.u.Flags.UserWritable == 1);
2721 InterlockedDecrement((volatile LONG*)&ControlArea->WritableUserReferences);
2722 }
2723
2724 /* Check if a user reference was added */
2725 if (UserRefIncremented)
2726 {
2727 /* Acquire the PFN lock while we change counters */
2728 OldIrql = MiAcquirePfnLock();
2729
2730 /* Decrement the accounting counters */
2731 ControlArea->NumberOfSectionReferences--;
2732 ASSERT((LONG)ControlArea->NumberOfUserReferences > 0);
2733 ControlArea->NumberOfUserReferences--;
2734
2735 /* Check if we should destroy the CA and release the lock */
2736 MiCheckControlArea(ControlArea, OldIrql);
2737 }
2738
2739 /* Return the failure code */
2740 return Status;
2741 }
2742
2743 /* NOTE: Past this point, all failures will be handled by Ob upon ref->0 */
2744
2745 /* Now copy the local section object from the stack into this new object */
2746 RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
2747 NewSection->Address.StartingVpn = 0;
2748
2749 /* For now, only user calls are supported */
2750 ASSERT(KernelCall == FALSE);
2751 NewSection->u.Flags.UserReference = TRUE;
2752
2753 /* Is this a "based" allocation, in which all mappings are identical? */
2754 if (AllocationAttributes & SEC_BASED)
2755 {
2756 /* Lock the VAD tree during the search */
2757 KeAcquireGuardedMutex(&MmSectionBasedMutex);
2758
2759 /* Is it a brand new ControArea ? */
2760 if (ControlArea->u.Flags.BeingCreated == 1)
2761 {
2762 ASSERT(ControlArea->u.Flags.Based == 1);
2763 /* Then we must find a global address, top-down */
2764 Status = MiFindEmptyAddressRangeDownBasedTree((SIZE_T)ControlArea->Segment->SizeOfSegment,
2765 (ULONG_PTR)MmHighSectionBase,
2766 _64K,
2767 &MmSectionBasedRoot,
2768 (ULONG_PTR*)&ControlArea->Segment->BasedAddress);
2769
2770 if (!NT_SUCCESS(Status))
2771 {
2772 /* No way to find a valid range. */
2773 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2774 ControlArea->u.Flags.Based = 0;
2775 NewSection->u.Flags.Based = 0;
2776 ObDereferenceObject(NewSection);
2777 return Status;
2778 }
2779
2780 /* Compute the ending address and insert it into the VAD tree */
2781 NewSection->Address.StartingVpn = (ULONG_PTR)ControlArea->Segment->BasedAddress;
2782 NewSection->Address.EndingVpn = NewSection->Address.StartingVpn + NewSection->SizeOfSection.LowPart - 1;
2783 MiInsertBasedSection(NewSection);
2784 }
2785 else
2786 {
2787 /* 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 !*/
2788 ASSERT(FALSE);
2789 }
2790
2791 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2792 }
2793
2794 /* The control area is not being created anymore */
2795 if (ControlArea->u.Flags.BeingCreated == 1)
2796 {
2797 /* Acquire the PFN lock while we set control area flags */
2798 OldIrql = MiAcquirePfnLock();
2799
2800 /* Take off the being created flag, and then release the lock */
2801 ControlArea->u.Flags.BeingCreated = 0;
2802 NewSection->u.Flags.BeingCreated = 0;
2803
2804 MiReleasePfnLock(OldIrql);
2805 }
2806
2807 /* Migrate the attribute into a flag */
2808 if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE;
2809
2810 /* If R/W access is not requested, this might eventually become a CoW mapping */
2811 if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
2812 {
2813 NewSection->u.Flags.CopyOnWrite = TRUE;
2814 }
2815
2816 /* Write down if this was a kernel call */
2817 ControlArea->u.Flags.WasPurged |= KernelCall;
2818 ASSERT(ControlArea->u.Flags.WasPurged == FALSE);
2819
2820 /* Make sure the segment and the section are the same size, or the section is smaller */
2821 ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment);
2822
2823 /* Return the object and the creation status */
2824 *SectionObject = (PVOID)NewSection;
2825 return Status;
2826 }
2827
2828 /*
2829 * @implemented
2830 */
2831 NTSTATUS
2832 NTAPI
2833 MmMapViewOfArm3Section(IN PVOID SectionObject,
2834 IN PEPROCESS Process,
2835 IN OUT PVOID *BaseAddress,
2836 IN ULONG_PTR ZeroBits,
2837 IN SIZE_T CommitSize,
2838 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
2839 IN OUT PSIZE_T ViewSize,
2840 IN SECTION_INHERIT InheritDisposition,
2841 IN ULONG AllocationType,
2842 IN ULONG Protect)
2843 {
2844 KAPC_STATE ApcState;
2845 BOOLEAN Attached = FALSE;
2846 PSECTION Section;
2847 PCONTROL_AREA ControlArea;
2848 ULONG ProtectionMask;
2849 NTSTATUS Status;
2850 PAGED_CODE();
2851
2852 /* Get the segment and control area */
2853 Section = (PSECTION)SectionObject;
2854 ControlArea = Section->Segment->ControlArea;
2855
2856 /* These flags/states are not yet supported by ARM3 */
2857 ASSERT(Section->u.Flags.Image == 0);
2858 ASSERT(Section->u.Flags.NoCache == 0);
2859 ASSERT(Section->u.Flags.WriteCombined == 0);
2860 ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2861
2862 /* FIXME */
2863 if ((AllocationType & MEM_RESERVE) != 0)
2864 {
2865 DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n");
2866 return STATUS_NOT_IMPLEMENTED;
2867 }
2868
2869 /* Check if the mapping protection is compatible with the create */
2870 if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2871 {
2872 DPRINT1("Mapping protection is incompatible\n");
2873 return STATUS_SECTION_PROTECTION;
2874 }
2875
2876 /* Check if the offset and size would cause an overflow */
2877 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2878 (ULONG64)SectionOffset->QuadPart)
2879 {
2880 DPRINT1("Section offset overflows\n");
2881 return STATUS_INVALID_VIEW_SIZE;
2882 }
2883
2884 /* Check if the offset and size are bigger than the section itself */
2885 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2886 (ULONG64)Section->SizeOfSection.QuadPart)
2887 {
2888 DPRINT1("Section offset is larger than section\n");
2889 return STATUS_INVALID_VIEW_SIZE;
2890 }
2891
2892 /* Check if the caller did not specify a view size */
2893 if (!(*ViewSize))
2894 {
2895 /* Compute it for the caller */
2896 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
2897
2898 /* Check if it's larger than 4GB or overflows into kernel-mode */
2899 if ((*ViewSize > 0xFFFFFFFF) ||
2900 (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize))
2901 {
2902 DPRINT1("Section view won't fit\n");
2903 return STATUS_INVALID_VIEW_SIZE;
2904 }
2905 }
2906
2907 /* Check if the commit size is larger than the view size */
2908 if (CommitSize > *ViewSize)
2909 {
2910 DPRINT1("Attempting to commit more than the view itself\n");
2911 return STATUS_INVALID_PARAMETER_5;
2912 }
2913
2914 /* Check if the view size is larger than the section */
2915 if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2916 {
2917 DPRINT1("The view is larger than the section\n");
2918 return STATUS_INVALID_VIEW_SIZE;
2919 }
2920
2921 /* Compute and validate the protection mask */
2922 ProtectionMask = MiMakeProtectionMask(Protect);
2923 if (ProtectionMask == MM_INVALID_PROTECTION)
2924 {
2925 DPRINT1("The protection is invalid\n");
2926 return STATUS_INVALID_PAGE_PROTECTION;
2927 }
2928
2929 /* We only handle pagefile-backed sections, which cannot be writecombined */
2930 if (Protect & PAGE_WRITECOMBINE)
2931 {
2932 DPRINT1("Cannot write combine a pagefile-backed section\n");
2933 return STATUS_INVALID_PARAMETER_10;
2934 }
2935
2936 /* Start by attaching to the current process if needed */
2937 if (PsGetCurrentProcess() != Process)
2938 {
2939 KeStackAttachProcess(&Process->Pcb, &ApcState);
2940 Attached = TRUE;
2941 }
2942
2943 /* Do the actual mapping */
2944 Status = MiMapViewOfDataSection(ControlArea,
2945 Process,
2946 BaseAddress,
2947 SectionOffset,
2948 ViewSize,
2949 Section,
2950 InheritDisposition,
2951 ProtectionMask,
2952 CommitSize,
2953 ZeroBits,
2954 AllocationType);
2955
2956 /* Detach if needed, then return status */
2957 if (Attached) KeUnstackDetachProcess(&ApcState);
2958 return Status;
2959 }
2960
2961 /*
2962 * @unimplemented
2963 */
2964 BOOLEAN
2965 NTAPI
2966 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2967 {
2968 UNIMPLEMENTED;
2969 return FALSE;
2970 }
2971
2972 /*
2973 * @unimplemented
2974 */
2975 BOOLEAN
2976 NTAPI
2977 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2978 IN BOOLEAN DelayClose)
2979 {
2980 UNIMPLEMENTED;
2981 return FALSE;
2982 }
2983
2984 /*
2985 * @implemented
2986 */
2987 NTSTATUS
2988 NTAPI
2989 MmMapViewInSessionSpace(IN PVOID Section,
2990 OUT PVOID *MappedBase,
2991 IN OUT PSIZE_T ViewSize)
2992 {
2993 PAGED_CODE();
2994
2995 // HACK
2996 if (MiIsRosSectionObject(Section))
2997 {
2998 return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
2999 }
3000
3001 /* Process must be in a session */
3002 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3003 {
3004 DPRINT1("Process is not in session\n");
3005 return STATUS_NOT_MAPPED_VIEW;
3006 }
3007
3008 /* Use the system space API, but with the session view instead */
3009 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3010 return MiMapViewInSystemSpace(Section,
3011 &MmSessionSpace->Session,
3012 MappedBase,
3013 ViewSize);
3014 }
3015
3016 /*
3017 * @implemented
3018 */
3019 NTSTATUS
3020 NTAPI
3021 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
3022 {
3023 PAGED_CODE();
3024
3025 // HACK
3026 if (!MI_IS_SESSION_ADDRESS(MappedBase))
3027 {
3028 return MmUnmapViewInSystemSpace(MappedBase);
3029 }
3030
3031 /* Process must be in a session */
3032 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3033 {
3034 DPRINT1("Proess is not in session\n");
3035 return STATUS_NOT_MAPPED_VIEW;
3036 }
3037
3038 /* Use the system space API, but with the session view instead */
3039 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3040 return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
3041 MappedBase);
3042 }
3043
3044 /*
3045 * @implemented
3046 */
3047 NTSTATUS
3048 NTAPI
3049 MmUnmapViewOfSection(IN PEPROCESS Process,
3050 IN PVOID BaseAddress)
3051 {
3052 return MiUnmapViewOfSection(Process, BaseAddress, 0);
3053 }
3054
3055 /*
3056 * @implemented
3057 */
3058 NTSTATUS
3059 NTAPI
3060 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
3061 {
3062 PMEMORY_AREA MemoryArea;
3063 PAGED_CODE();
3064
3065 /* Was this mapped by RosMm? */
3066 MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
3067 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
3068 {
3069 return MiRosUnmapViewInSystemSpace(MappedBase);
3070 }
3071
3072 /* It was not, call the ARM3 routine */
3073 return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
3074 }
3075
3076 /*
3077 * @implemented
3078 */
3079 NTSTATUS
3080 NTAPI
3081 MmCommitSessionMappedView(IN PVOID MappedBase,
3082 IN SIZE_T ViewSize)
3083 {
3084 ULONG_PTR StartAddress, EndingAddress, Base;
3085 ULONG Hash, Count = 0, Size, QuotaCharge;
3086 PMMSESSION Session;
3087 PMMPTE LastProtoPte, PointerPte, ProtoPte;
3088 PCONTROL_AREA ControlArea;
3089 PSEGMENT Segment;
3090 PSUBSECTION Subsection;
3091 MMPTE TempPte;
3092 PAGED_CODE();
3093
3094 /* Make sure the base isn't past the session view range */
3095 if ((MappedBase < MiSessionViewStart) ||
3096 (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
3097 {
3098 DPRINT1("Base outside of valid range\n");
3099 return STATUS_INVALID_PARAMETER_1;
3100 }
3101
3102 /* Make sure the size isn't past the session view range */
3103 if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
3104 (ULONG_PTR)MappedBase) < ViewSize)
3105 {
3106 DPRINT1("Size outside of valid range\n");
3107 return STATUS_INVALID_PARAMETER_2;
3108 }
3109
3110 /* Sanity check */
3111 ASSERT(ViewSize != 0);
3112
3113 /* Process must be in a session */
3114 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
3115 {
3116 DPRINT1("Process is not in session\n");
3117 return STATUS_NOT_MAPPED_VIEW;
3118 }
3119
3120 /* Compute the correctly aligned base and end addresses */
3121 StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
3122 EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
3123
3124 /* Sanity check and grab the session */
3125 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
3126 Session = &MmSessionSpace->Session;
3127
3128 /* Get the hash entry for this allocation */
3129 Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
3130
3131 /* Lock system space */
3132 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
3133
3134 /* Loop twice so we can try rolling over if needed */
3135 while (TRUE)
3136 {
3137 /* Extract the size and base addresses from the entry */
3138 Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
3139 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
3140
3141 /* Convert the size to bucket chunks */
3142 Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
3143
3144 /* Bail out if this entry fits in here */
3145 if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
3146
3147 /* Check if we overflew past the end of the hash table */
3148 if (++Hash >= Session->SystemSpaceHashSize)
3149 {
3150 /* Reset the hash to zero and keep searching from the bottom */
3151 Hash = 0;
3152 if (++Count == 2)
3153 {
3154 /* But if we overflew twice, then this is not a real mapping */
3155 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
3156 Base,
3157 2,
3158 0,
3159 0);
3160 }
3161 }
3162 }
3163
3164 /* Make sure the view being mapped is not file-based */
3165 ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
3166 if (ControlArea->FilePointer != NULL)
3167 {
3168 /* It is, so we have to bail out */
3169 DPRINT1("Only page-filed backed sections can be commited\n");
3170 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3171 return STATUS_ALREADY_COMMITTED;
3172 }
3173
3174 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
3175 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
3176 ASSERT(ControlArea->u.Flags.Rom == 0);
3177 Subsection = (PSUBSECTION)(ControlArea + 1);
3178
3179 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
3180 ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
3181 QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
3182 LastProtoPte = ProtoPte + QuotaCharge;
3183 if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
3184 {
3185 DPRINT1("PTE is out of bounds\n");
3186 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3187 return STATUS_INVALID_PARAMETER_2;
3188 }
3189
3190 /* Acquire the commit lock and count all the non-committed PTEs */
3191 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
3192 PointerPte = ProtoPte;
3193 while (PointerPte < LastProtoPte)
3194 {
3195 if (PointerPte->u.Long) QuotaCharge--;
3196 PointerPte++;
3197 }
3198
3199 /* Was everything committed already? */
3200 if (!QuotaCharge)
3201 {
3202 /* Nothing to do! */
3203 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3204 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3205 return STATUS_SUCCESS;
3206 }
3207
3208 /* Pick the segment and template PTE */
3209 Segment = ControlArea->Segment;
3210 TempPte = Segment->SegmentPteTemplate;
3211 ASSERT(TempPte.u.Long != 0);
3212
3213 /* Loop all prototype PTEs to be committed */
3214 PointerPte = ProtoPte;
3215 while (PointerPte < LastProtoPte)
3216 {
3217 /* Make sure the PTE is already invalid */
3218 if (PointerPte->u.Long == 0)
3219 {
3220 /* And write the invalid PTE */
3221 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3222 }
3223
3224 /* Move to the next PTE */
3225 PointerPte++;
3226 }
3227
3228 /* Check if we had at least one page charged */
3229 if (QuotaCharge)
3230 {
3231 /* Update the accounting data */
3232 Segment->NumberOfCommittedPages += QuotaCharge;
3233 InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3234 }
3235
3236 /* Release all */
3237 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3238 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3239 return STATUS_SUCCESS;
3240 }
3241
3242 VOID
3243 NTAPI
3244 MiDeleteARM3Section(PVOID ObjectBody)
3245 {
3246 PSECTION SectionObject;
3247 PCONTROL_AREA ControlArea;
3248 KIRQL OldIrql;
3249
3250 SectionObject = (PSECTION)ObjectBody;
3251
3252 if (SectionObject->u.Flags.Based == 1)
3253 {
3254 /* Remove the node from the global section address tree */
3255 KeAcquireGuardedMutex(&MmSectionBasedMutex);
3256 MiRemoveNode(&SectionObject->Address, &MmSectionBasedRoot);
3257 KeReleaseGuardedMutex(&MmSectionBasedMutex);
3258 }
3259
3260 /* Lock the PFN database */
3261 OldIrql = MiAcquirePfnLock();
3262
3263 ASSERT(SectionObject->Segment);
3264 ASSERT(SectionObject->Segment->ControlArea);
3265
3266 ControlArea = SectionObject->Segment->ControlArea;
3267
3268 /* Dereference */
3269 ControlArea->NumberOfSectionReferences--;
3270 ControlArea->NumberOfUserReferences--;
3271
3272 ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
3273
3274 /* Check it. It will delete it if there is no more reference to it */
3275 MiCheckControlArea(ControlArea, OldIrql);
3276 }
3277
3278 ULONG
3279 NTAPI
3280 MmDoesFileHaveUserWritableReferences(IN PSECTION_OBJECT_POINTERS SectionPointer)
3281 {
3282 UNIMPLEMENTED;
3283 return 0;
3284 }
3285
3286 /* SYSTEM CALLS ***************************************************************/
3287
3288 NTSTATUS
3289 NTAPI
3290 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3291 IN PVOID File2MappedAsFile)
3292 {
3293 PVOID AddressSpace;
3294 PMMVAD Vad1, Vad2;
3295 PFILE_OBJECT FileObject1, FileObject2;
3296 NTSTATUS Status;
3297
3298 /* Lock address space */
3299 AddressSpace = MmGetCurrentAddressSpace();
3300 MmLockAddressSpace(AddressSpace);
3301
3302 /* Get the VAD for Address 1 */
3303 Vad1 = MiLocateAddress(File1MappedAsAnImage);
3304 if (Vad1 == NULL)
3305 {
3306 /* Fail, the address does not exist */
3307 DPRINT1("No VAD at address 1 %p\n", File1MappedAsAnImage);
3308 Status = STATUS_INVALID_ADDRESS;
3309 goto Exit;
3310 }
3311
3312 /* Get the VAD for Address 2 */
3313 Vad2 = MiLocateAddress(File2MappedAsFile);
3314 if (Vad2 == NULL)
3315 {
3316 /* Fail, the address does not exist */
3317 DPRINT1("No VAD at address 2 %p\n", File2MappedAsFile);
3318 Status = STATUS_INVALID_ADDRESS;
3319 goto Exit;
3320 }
3321
3322 /* Get the file object pointer for VAD 1 */
3323 FileObject1 = MiGetFileObjectForVad(Vad1);
3324 if (FileObject1 == NULL)
3325 {
3326 DPRINT1("Failed to get file object for Address 1 %p\n", File1MappedAsAnImage);
3327 Status = STATUS_CONFLICTING_ADDRESSES;
3328 goto Exit;
3329 }
3330
3331 /* Get the file object pointer for VAD 2 */
3332 FileObject2 = MiGetFileObjectForVad(Vad2);
3333 if (FileObject2 == NULL)
3334 {
3335 DPRINT1("Failed to get file object for Address 2 %p\n", File2MappedAsFile);
3336 Status = STATUS_CONFLICTING_ADDRESSES;
3337 goto Exit;
3338 }
3339
3340 /* Make sure Vad1 is an image mapping */
3341 if (Vad1->u.VadFlags.VadType != VadImageMap)
3342 {
3343 DPRINT1("Address 1 (%p) is not an image mapping\n", File1MappedAsAnImage);
3344 Status = STATUS_NOT_SAME_DEVICE;
3345 goto Exit;
3346 }
3347
3348 /* SectionObjectPointer is equal if the files are equal */
3349 if (FileObject1->SectionObjectPointer == FileObject2->SectionObjectPointer)
3350 {
3351 Status = STATUS_SUCCESS;
3352 }
3353 else
3354 {
3355 Status = STATUS_NOT_SAME_DEVICE;
3356 }
3357
3358 Exit:
3359 /* Unlock address space */
3360 MmUnlockAddressSpace(AddressSpace);
3361 return Status;
3362 }
3363
3364 /*
3365 * @implemented
3366 */
3367 NTSTATUS
3368 NTAPI
3369 NtCreateSection(OUT PHANDLE SectionHandle,
3370 IN ACCESS_MASK DesiredAccess,
3371 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3372 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3373 IN ULONG SectionPageProtection OPTIONAL,
3374 IN ULONG AllocationAttributes,
3375 IN HANDLE FileHandle OPTIONAL)
3376 {
3377 LARGE_INTEGER SafeMaximumSize;
3378 PVOID SectionObject;
3379 HANDLE Handle;
3380 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3381 NTSTATUS Status;
3382 PAGED_CODE();
3383
3384 /* Check for non-existing flags */
3385 if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
3386 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
3387 SEC_NO_CHANGE)))
3388 {
3389 if (!(AllocationAttributes & 1))
3390 {
3391 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
3392 return STATUS_INVALID_PARAMETER_6;
3393 }
3394 }
3395
3396 /* Check for no allocation type */
3397 if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
3398 {
3399 DPRINT1("Missing allocation type in allocation attributes\n");
3400 return STATUS_INVALID_PARAMETER_6;
3401 }
3402
3403 /* Check for image allocation with invalid attributes */
3404 if ((AllocationAttributes & SEC_IMAGE) &&
3405 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
3406 SEC_NOCACHE | SEC_NO_CHANGE)))
3407 {
3408 DPRINT1("Image allocation with invalid attributes\n");
3409 return STATUS_INVALID_PARAMETER_6;
3410 }
3411
3412 /* Check for allocation type is both commit and reserve */
3413 if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
3414 {
3415 DPRINT1("Commit and reserve in the same time\n");
3416 return STATUS_INVALID_PARAMETER_6;
3417 }
3418
3419 /* Now check for valid protection */
3420 if ((SectionPageProtection & PAGE_NOCACHE) ||