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