Sync with trunk r63647.
[reactos.git] / ntoskrnl / mm / ARM3 / section.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/section.c
5 * PURPOSE: ARM Memory Manager Section Support
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../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 MMPTE TempPte;
612 KIRQL OldIrql;
613
614 /* Capture data */
615 SegmentFlags = Segment->SegmentFlags;
616 ControlArea = Segment->ControlArea;
617
618 /* Make sure control area is on the right delete path */
619 ASSERT(ControlArea->u.Flags.BeingDeleted == 1);
620 ASSERT(ControlArea->WritableUserReferences == 0);
621
622 /* These things are not supported yet */
623 ASSERT(ControlArea->DereferenceList.Flink == NULL);
624 ASSERT(!(ControlArea->u.Flags.Image) & !(ControlArea->u.Flags.File));
625 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
626 ASSERT(ControlArea->u.Flags.Rom == 0);
627
628 /* Get the subsection and PTEs for this segment */
629 Subsection = (PSUBSECTION)(ControlArea + 1);
630 PointerPte = Subsection->SubsectionBase;
631 LastPte = PointerPte + Segment->NonExtendedPtes;
632
633 /* Lock the PFN database */
634 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
635
636 /* Check if the master PTE is invalid */
637 PteForProto = MiAddressToPte(PointerPte);
638 if (!PteForProto->u.Hard.Valid)
639 {
640 /* Fault it in */
641 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
642 }
643
644 /* Loop all the segment PTEs */
645 while (PointerPte < LastPte)
646 {
647 /* Check if it's time to switch master PTEs if we passed a PDE boundary */
648 if (!((ULONG_PTR)PointerPte & (PD_SIZE - 1)) &&
649 (PointerPte != Subsection->SubsectionBase))
650 {
651 /* Check if the master PTE is invalid */
652 PteForProto = MiAddressToPte(PointerPte);
653 if (!PteForProto->u.Hard.Valid)
654 {
655 /* Fault it in */
656 MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
657 }
658 }
659
660 /* This should be a prototype PTE */
661 TempPte = *PointerPte;
662 ASSERT(SegmentFlags.LargePages == 0);
663 ASSERT(TempPte.u.Hard.Valid == 0);
664 ASSERT(TempPte.u.Soft.Prototype == 1);
665
666 /* Zero the PTE and keep going */
667 PointerPte->u.Long = 0;
668 PointerPte++;
669 }
670
671 /* Release the PFN lock */
672 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
673
674 /* Free the structures */
675 ExFreePool(ControlArea);
676 ExFreePool(Segment);
677 }
678
679 VOID
680 NTAPI
681 MiCheckControlArea(IN PCONTROL_AREA ControlArea,
682 IN KIRQL OldIrql)
683 {
684 BOOLEAN DeleteSegment = FALSE;
685 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
686
687 /* Check if this is the last reference or view */
688 if (!(ControlArea->NumberOfMappedViews) &&
689 !(ControlArea->NumberOfSectionReferences))
690 {
691 /* There should be no more user references either */
692 ASSERT(ControlArea->NumberOfUserReferences == 0);
693
694 /* Not yet supported */
695 ASSERT(ControlArea->FilePointer == NULL);
696
697 /* The control area is being destroyed */
698 ControlArea->u.Flags.BeingDeleted = TRUE;
699 DeleteSegment = TRUE;
700 }
701
702 /* Release the PFN lock */
703 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
704
705 /* Delete the segment if needed */
706 if (DeleteSegment)
707 {
708 /* No more user write references at all */
709 ASSERT(ControlArea->WritableUserReferences == 0);
710 MiSegmentDelete(ControlArea->Segment);
711 }
712 }
713
714 VOID
715 NTAPI
716 MiDereferenceControlArea(IN PCONTROL_AREA ControlArea)
717 {
718 KIRQL OldIrql;
719
720 /* Lock the PFN database */
721 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
722
723 /* Drop reference counts */
724 ControlArea->NumberOfMappedViews--;
725 ControlArea->NumberOfUserReferences--;
726
727 /* Check if it's time to delete the CA. This releases the lock */
728 MiCheckControlArea(ControlArea, OldIrql);
729 }
730
731 VOID
732 NTAPI
733 MiRemoveMappedView(IN PEPROCESS CurrentProcess,
734 IN PMMVAD Vad)
735 {
736 KIRQL OldIrql;
737 PCONTROL_AREA ControlArea;
738 PETHREAD CurrentThread = PsGetCurrentThread();
739
740 /* Get the control area */
741 ControlArea = Vad->ControlArea;
742
743 /* We only support non-extendable, non-image, pagefile-backed regular sections */
744 ASSERT(Vad->u.VadFlags.VadType == VadNone);
745 ASSERT(Vad->u2.VadFlags2.ExtendableFile == FALSE);
746 ASSERT(ControlArea);
747 ASSERT(ControlArea->FilePointer == NULL);
748
749 /* Delete the actual virtual memory pages */
750 MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT,
751 (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1),
752 Vad);
753
754 /* Release the working set */
755 MiUnlockProcessWorkingSetUnsafe(CurrentProcess, CurrentThread);
756
757 /* Lock the PFN database */
758 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
759
760 /* Remove references */
761 ControlArea->NumberOfMappedViews--;
762 ControlArea->NumberOfUserReferences--;
763
764 /* Check if it should be destroyed */
765 MiCheckControlArea(ControlArea, OldIrql);
766 }
767
768 NTSTATUS
769 NTAPI
770 MiUnmapViewOfSection(IN PEPROCESS Process,
771 IN PVOID BaseAddress,
772 IN ULONG Flags)
773 {
774 PMEMORY_AREA MemoryArea;
775 BOOLEAN Attached = FALSE;
776 KAPC_STATE ApcState;
777 PMMVAD Vad;
778 PVOID DbgBase = NULL;
779 SIZE_T RegionSize;
780 NTSTATUS Status;
781 PETHREAD CurrentThread = PsGetCurrentThread();
782 PEPROCESS CurrentProcess = PsGetCurrentProcess();
783 PAGED_CODE();
784
785 /* Check for Mm Region */
786 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, BaseAddress);
787 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
788 {
789 /* Call Mm API */
790 return MiRosUnmapViewOfSection(Process, BaseAddress, Flags);
791 }
792
793 /* Check if we should attach to the process */
794 if (CurrentProcess != Process)
795 {
796 /* The process is different, do an attach */
797 KeStackAttachProcess(&Process->Pcb, &ApcState);
798 Attached = TRUE;
799 }
800
801 /* Check if we need to lock the address space */
802 if (!Flags) MmLockAddressSpace(&Process->Vm);
803
804 /* Check if the process is already daed */
805 if (Process->VmDeleted)
806 {
807 /* Fail the call */
808 DPRINT1("Process died!\n");
809 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
810 Status = STATUS_PROCESS_IS_TERMINATING;
811 goto Quickie;
812 }
813
814 /* Find the VAD for the address and make sure it's a section VAD */
815 Vad = MiLocateAddress(BaseAddress);
816 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory))
817 {
818 /* Couldn't find it, or invalid VAD, fail */
819 DPRINT1("No VAD or invalid VAD\n");
820 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
821 Status = STATUS_NOT_MAPPED_VIEW;
822 goto Quickie;
823 }
824
825 /* We should be attached */
826 ASSERT(Process == PsGetCurrentProcess());
827
828 /* We need the base address for the debugger message on image-backed VADs */
829 if (Vad->u.VadFlags.VadType == VadImageMap)
830 {
831 DbgBase = (PVOID)(Vad->StartingVpn >> PAGE_SHIFT);
832 }
833
834 /* Compute the size of the VAD region */
835 RegionSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
836
837 /* For SEC_NO_CHANGE sections, we need some extra checks */
838 if (Vad->u.VadFlags.NoChange == 1)
839 {
840 /* Are we allowed to mess with this VAD? */
841 Status = MiCheckSecuredVad(Vad,
842 (PVOID)(Vad->StartingVpn >> PAGE_SHIFT),
843 RegionSize,
844 MM_DELETE_CHECK);
845 if (!NT_SUCCESS(Status))
846 {
847 /* We failed */
848 DPRINT1("Trying to unmap protected VAD!\n");
849 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
850 goto Quickie;
851 }
852 }
853
854 /* Not currently supported */
855 ASSERT(Vad->u.VadFlags.VadType != VadRotatePhysical);
856
857 /* FIXME: Remove VAD charges */
858
859 /* Lock the working set */
860 MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
861
862 /* Remove the VAD */
863 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
864 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
865
866 /* Remove the PTEs for this view, which also releases the working set lock */
867 MiRemoveMappedView(Process, Vad);
868
869 /* FIXME: Remove commitment */
870
871 /* Update performance counter and release the lock */
872 Process->VirtualSize -= RegionSize;
873 if (!Flags) MmUnlockAddressSpace(&Process->Vm);
874
875 /* Destroy the VAD and return success */
876 ExFreePool(Vad);
877 Status = STATUS_SUCCESS;
878
879 /* Failure and success case -- send debugger message, detach, and return */
880 Quickie:
881 if (DbgBase) DbgkUnMapViewOfSection(DbgBase);
882 if (Attached) KeUnstackDetachProcess(&ApcState);
883 return Status;
884 }
885
886 NTSTATUS
887 NTAPI
888 MiSessionCommitPageTables(IN PVOID StartVa,
889 IN PVOID EndVa)
890 {
891 KIRQL OldIrql;
892 ULONG Color, Index;
893 PMMPTE StartPde, EndPde;
894 MMPTE TempPte = ValidKernelPdeLocal;
895 PMMPFN Pfn1;
896 PFN_NUMBER PageCount = 0, ActualPages = 0, PageFrameNumber;
897
898 /* Windows sanity checks */
899 ASSERT(StartVa >= (PVOID)MmSessionBase);
900 ASSERT(EndVa < (PVOID)MiSessionSpaceEnd);
901 ASSERT(PAGE_ALIGN(EndVa) == EndVa);
902
903 /* Get the start and end PDE, then loop each one */
904 StartPde = MiAddressToPde(StartVa);
905 EndPde = MiAddressToPde((PVOID)((ULONG_PTR)EndVa - 1));
906 Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22;
907 while (StartPde <= EndPde)
908 {
909 #ifndef _M_AMD64
910 /* If we don't already have a page table for it, increment count */
911 if (MmSessionSpace->PageTables[Index].u.Long == 0) PageCount++;
912 #endif
913 /* Move to the next one */
914 StartPde++;
915 Index++;
916 }
917
918 /* If there's no page tables to create, bail out */
919 if (PageCount == 0) return STATUS_SUCCESS;
920
921 /* Reset the start PDE and index */
922 StartPde = MiAddressToPde(StartVa);
923 Index = ((ULONG_PTR)StartVa - (ULONG_PTR)MmSessionBase) >> 22;
924
925 /* Loop each PDE while holding the working set lock */
926 // MiLockWorkingSet(PsGetCurrentThread(),
927 // &MmSessionSpace->GlobalVirtualAddress->Vm);
928 #ifndef _M_AMD64
929 while (StartPde <= EndPde)
930 {
931 /* Check if we already have a page table */
932 if (MmSessionSpace->PageTables[Index].u.Long == 0)
933 {
934 /* We don't, so the PDE shouldn't be ready yet */
935 ASSERT(StartPde->u.Hard.Valid == 0);
936
937 /* ReactOS check to avoid MiEnsureAvailablePageOrWait */
938 ASSERT(MmAvailablePages >= 32);
939
940 /* Acquire the PFN lock and grab a zero page */
941 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
942 Color = (++MmSessionSpace->Color) & MmSecondaryColorMask;
943 PageFrameNumber = MiRemoveZeroPage(Color);
944 TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
945 MI_WRITE_VALID_PTE(StartPde, TempPte);
946
947 /* Write the page table in session space structure */
948 ASSERT(MmSessionSpace->PageTables[Index].u.Long == 0);
949 MmSessionSpace->PageTables[Index] = TempPte;
950
951 /* Initialize the PFN */
952 MiInitializePfnForOtherProcess(PageFrameNumber,
953 StartPde,
954 MmSessionSpace->SessionPageDirectoryIndex);
955
956 /* And now release the lock */
957 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
958
959 /* Get the PFN entry and make sure there's no event for it */
960 Pfn1 = MI_PFN_ELEMENT(PageFrameNumber);
961 ASSERT(Pfn1->u1.Event == NULL);
962
963 /* Increment the number of pages */
964 ActualPages++;
965 }
966
967 /* Move to the next PDE */
968 StartPde++;
969 Index++;
970 }
971 #endif
972
973 /* Make sure we didn't do more pages than expected */
974 ASSERT(ActualPages <= PageCount);
975
976 /* Release the working set lock */
977 // MiUnlockWorkingSet(PsGetCurrentThread(),
978 // &MmSessionSpace->GlobalVirtualAddress->Vm);
979
980
981 /* If we did at least one page... */
982 if (ActualPages)
983 {
984 /* Update the performance counters! */
985 InterlockedExchangeAddSizeT(&MmSessionSpace->NonPageablePages, ActualPages);
986 InterlockedExchangeAddSizeT(&MmSessionSpace->CommittedPages, ActualPages);
987 }
988
989 /* Return status */
990 return STATUS_SUCCESS;
991 }
992
993 NTSTATUS
994 NTAPI
995 MiMapViewInSystemSpace(IN PVOID Section,
996 IN PMMSESSION Session,
997 OUT PVOID *MappedBase,
998 IN OUT PSIZE_T ViewSize)
999 {
1000 PVOID Base;
1001 PCONTROL_AREA ControlArea;
1002 ULONG Buckets, SectionSize;
1003 NTSTATUS Status;
1004 PAGED_CODE();
1005
1006 /* Get the control area, check for any flags ARM3 doesn't yet support */
1007 ControlArea = ((PSECTION)Section)->Segment->ControlArea;
1008 ASSERT(ControlArea->u.Flags.Image == 0);
1009 ASSERT(ControlArea->FilePointer == NULL);
1010 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1011 ASSERT(ControlArea->u.Flags.Rom == 0);
1012 ASSERT(ControlArea->u.Flags.WasPurged == 0);
1013
1014 /* Increase the reference and map count on the control area, no purges yet */
1015 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1016 ASSERT(NT_SUCCESS(Status));
1017
1018 /* Get the section size at creation time */
1019 SectionSize = ((PSECTION)Section)->SizeOfSection.LowPart;
1020
1021 /* If the caller didn't specify a view size, assume the whole section */
1022 if (!(*ViewSize)) *ViewSize = SectionSize;
1023
1024 /* Check if the caller wanted a larger section than the view */
1025 if (*ViewSize > SectionSize)
1026 {
1027 /* Fail */
1028 DPRINT1("View is too large\n");
1029 MiDereferenceControlArea(ControlArea);
1030 return STATUS_INVALID_VIEW_SIZE;
1031 }
1032
1033 /* Get the number of 64K buckets required for this mapping */
1034 Buckets = (ULONG)(*ViewSize / MI_SYSTEM_VIEW_BUCKET_SIZE);
1035 if (*ViewSize & (MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) Buckets++;
1036
1037 /* Check if the view is more than 4GB large */
1038 if (Buckets >= MI_SYSTEM_VIEW_BUCKET_SIZE)
1039 {
1040 /* Fail */
1041 DPRINT1("View is too large\n");
1042 MiDereferenceControlArea(ControlArea);
1043 return STATUS_INVALID_VIEW_SIZE;
1044 }
1045
1046 /* Insert this view into system space and get a base address for it */
1047 Base = MiInsertInSystemSpace(Session, Buckets, ControlArea);
1048 if (!Base)
1049 {
1050 /* Fail */
1051 DPRINT1("Out of system space\n");
1052 MiDereferenceControlArea(ControlArea);
1053 return STATUS_NO_MEMORY;
1054 }
1055
1056 /* What's the underlying session? */
1057 if (Session == &MmSession)
1058 {
1059 /* Create the PDEs needed for this mapping, and double-map them if needed */
1060 MiFillSystemPageDirectory(Base, Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE);
1061 Status = STATUS_SUCCESS;
1062 }
1063 else
1064 {
1065 /* Create the PDEs needed for this mapping */
1066 Status = MiSessionCommitPageTables(Base,
1067 (PVOID)((ULONG_PTR)Base +
1068 Buckets * MI_SYSTEM_VIEW_BUCKET_SIZE));
1069 NT_ASSERT(NT_SUCCESS(Status));
1070 }
1071
1072 /* Create the actual prototype PTEs for this mapping */
1073 Status = MiAddMappedPtes(MiAddressToPte(Base),
1074 BYTES_TO_PAGES(*ViewSize),
1075 ControlArea);
1076 ASSERT(NT_SUCCESS(Status));
1077
1078 /* Return the base adress of the mapping and success */
1079 *MappedBase = Base;
1080 return STATUS_SUCCESS;
1081 }
1082
1083 VOID
1084 NTAPI
1085 MiSetControlAreaSymbolsLoaded(IN PCONTROL_AREA ControlArea)
1086 {
1087 KIRQL OldIrql;
1088
1089 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1090
1091 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1092 ControlArea->u.Flags.DebugSymbolsLoaded |= 1;
1093
1094 ASSERT(OldIrql <= APC_LEVEL);
1095 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1096 ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
1097 }
1098
1099 VOID
1100 NTAPI
1101 MiLoadUserSymbols(IN PCONTROL_AREA ControlArea,
1102 IN PVOID BaseAddress,
1103 IN PEPROCESS Process)
1104 {
1105 NTSTATUS Status;
1106 ANSI_STRING FileNameA;
1107 PLIST_ENTRY NextEntry;
1108 PUNICODE_STRING FileName;
1109 PIMAGE_NT_HEADERS NtHeaders;
1110 PLDR_DATA_TABLE_ENTRY LdrEntry;
1111
1112 FileName = &ControlArea->FilePointer->FileName;
1113 if (FileName->Length == 0)
1114 {
1115 return;
1116 }
1117
1118 /* Acquire module list lock */
1119 KeEnterCriticalRegion();
1120 ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
1121
1122 /* Browse list to try to find current module */
1123 for (NextEntry = MmLoadedUserImageList.Flink;
1124 NextEntry != &MmLoadedUserImageList;
1125 NextEntry = NextEntry->Flink)
1126 {
1127 /* Get the entry */
1128 LdrEntry = CONTAINING_RECORD(NextEntry,
1129 LDR_DATA_TABLE_ENTRY,
1130 InLoadOrderLinks);
1131
1132 /* If already in the list, increase load count */
1133 if (LdrEntry->DllBase == BaseAddress)
1134 {
1135 ++LdrEntry->LoadCount;
1136 break;
1137 }
1138 }
1139
1140 /* Not in the list, we'll add it */
1141 if (NextEntry == &MmLoadedUserImageList)
1142 {
1143 /* Allocate our element, taking to the name string and its null char */
1144 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry), 'bDmM');
1145 if (LdrEntry)
1146 {
1147 memset(LdrEntry, 0, FileName->Length + sizeof(UNICODE_NULL) + sizeof(*LdrEntry));
1148
1149 _SEH2_TRY
1150 {
1151 /* Get image checksum and size */
1152 NtHeaders = RtlImageNtHeader(BaseAddress);
1153 if (NtHeaders)
1154 {
1155 LdrEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
1156 LdrEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
1157 }
1158 }
1159 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1160 {
1161 ExFreePoolWithTag(LdrEntry, 'bDmM');
1162 _SEH2_YIELD(return);
1163 }
1164 _SEH2_END;
1165
1166 /* Fill all the details */
1167 LdrEntry->DllBase = BaseAddress;
1168 LdrEntry->FullDllName.Buffer = (PVOID)((ULONG_PTR)LdrEntry + sizeof(*LdrEntry));
1169 LdrEntry->FullDllName.Length = FileName->Length;
1170 LdrEntry->FullDllName.MaximumLength = FileName->Length + sizeof(UNICODE_NULL);
1171 memcpy(LdrEntry->FullDllName.Buffer, FileName->Buffer, FileName->Length);
1172 LdrEntry->FullDllName.Buffer[LdrEntry->FullDllName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1173 LdrEntry->LoadCount = 1;
1174
1175 /* Insert! */
1176 InsertHeadList(&MmLoadedUserImageList, &LdrEntry->InLoadOrderLinks);
1177 }
1178 }
1179
1180 /* Release locks */
1181 ExReleaseResourceLite(&PsLoadedModuleResource);
1182 KeLeaveCriticalRegion();
1183
1184 /* Load symbols */
1185 Status = RtlUnicodeStringToAnsiString(&FileNameA, FileName, TRUE);
1186 if (NT_SUCCESS(Status))
1187 {
1188 DbgLoadImageSymbols(&FileNameA, BaseAddress, (ULONG_PTR)Process->UniqueProcessId);
1189 RtlFreeAnsiString(&FileNameA);
1190 }
1191 }
1192
1193 NTSTATUS
1194 NTAPI
1195 MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea,
1196 IN PEPROCESS Process,
1197 IN PVOID *BaseAddress,
1198 IN PLARGE_INTEGER SectionOffset,
1199 IN PSIZE_T ViewSize,
1200 IN PSECTION Section,
1201 IN SECTION_INHERIT InheritDisposition,
1202 IN ULONG ProtectionMask,
1203 IN SIZE_T CommitSize,
1204 IN ULONG_PTR ZeroBits,
1205 IN ULONG AllocationType)
1206 {
1207 PMMVAD_LONG Vad;
1208 PETHREAD Thread = PsGetCurrentThread();
1209 ULONG_PTR StartAddress, EndingAddress;
1210 PSUBSECTION Subsection;
1211 PSEGMENT Segment;
1212 PFN_NUMBER PteOffset;
1213 NTSTATUS Status;
1214 ULONG QuotaCharge = 0, QuotaExcess = 0;
1215 PMMPTE PointerPte, LastPte;
1216 MMPTE TempPte;
1217 PMMADDRESS_NODE Parent;
1218 TABLE_SEARCH_RESULT Result;
1219
1220 /* Get the segment for this section */
1221 Segment = ControlArea->Segment;
1222
1223 /* One can only reserve a file-based mapping, not shared memory! */
1224 if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer))
1225 {
1226 return STATUS_INVALID_PARAMETER_9;
1227 }
1228
1229 /* This flag determines alignment, but ARM3 does not yet support it */
1230 ASSERT((AllocationType & MEM_DOS_LIM) == 0);
1231
1232 /* First, increase the map count. No purging is supported yet */
1233 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1234 if (!NT_SUCCESS(Status)) return Status;
1235
1236 /* Check if the caller specified the view size */
1237 if (!(*ViewSize))
1238 {
1239 /* The caller did not, so pick a 64K aligned view size based on the offset */
1240 SectionOffset->LowPart &= ~(_64K - 1);
1241 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
1242 }
1243 else
1244 {
1245 /* A size was specified, align it to a 64K boundary */
1246 *ViewSize += SectionOffset->LowPart & (_64K - 1);
1247
1248 /* Align the offset as well to make this an aligned map */
1249 SectionOffset->LowPart &= ~((ULONG)_64K - 1);
1250 }
1251
1252 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */
1253 ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0);
1254
1255 /* It's illegal to try to map more than 2GB */
1256 /* FIXME: Should dereference the control area */
1257 if (*ViewSize >= 0x80000000) return STATUS_INVALID_VIEW_SIZE;
1258
1259 /* Windows ASSERTs for this flag */
1260 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1261
1262 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
1263 ASSERT(ControlArea->u.Flags.Rom == 0);
1264 Subsection = (PSUBSECTION)(ControlArea + 1);
1265
1266 /* Sections with extended segments are not supported in ARM3 */
1267 ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0);
1268
1269 /* Within this section, figure out which PTEs will describe the view */
1270 PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT);
1271
1272 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */
1273 ASSERT(PteOffset < Segment->TotalNumberOfPtes);
1274 ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset);
1275
1276 /* In ARM3, only one subsection is used for now. It must contain these PTEs */
1277 ASSERT(PteOffset < Subsection->PtesInSubsection);
1278
1279 /* In ARM3, only page-file backed sections (shared memory) are supported now */
1280 ASSERT(ControlArea->FilePointer == NULL);
1281
1282 /* Windows ASSERTs for this too -- there must be a subsection base address */
1283 ASSERT(Subsection->SubsectionBase != NULL);
1284
1285 /* Compute how much commit space the segment will take */
1286 if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes))
1287 {
1288 PointerPte = &Subsection->SubsectionBase[PteOffset];
1289 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
1290 QuotaCharge = (ULONG)(LastPte - PointerPte);
1291 }
1292
1293 /* ARM3 does not currently support large pages */
1294 ASSERT(Segment->SegmentFlags.LargePages == 0);
1295
1296 /* Did the caller specify an address? */
1297 if (!(*BaseAddress) && !(Section->Address.StartingVpn))
1298 {
1299 /* ARM3 does not support these flags yet */
1300 ASSERT(Process->VmTopDown == 0);
1301 ASSERT(ZeroBits == 0);
1302
1303 /* Which way should we search? */
1304 if (AllocationType & MEM_TOP_DOWN)
1305 {
1306 /* No, find an address top-down */
1307 Result = MiFindEmptyAddressRangeDownTree(*ViewSize,
1308 (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS,
1309 _64K,
1310 &Process->VadRoot,
1311 &StartAddress,
1312 &Parent);
1313 }
1314 else
1315 {
1316 /* No, find an address bottom-up */
1317 Result = MiFindEmptyAddressRangeInTree(*ViewSize,
1318 _64K,
1319 &Process->VadRoot,
1320 &Parent,
1321 &StartAddress);
1322 }
1323
1324 /* Check if we found a suitable location */
1325 if (Result == TableFoundNode)
1326 {
1327 DPRINT1("Not enough free space to insert this section!\n");
1328 MiDereferenceControlArea(ControlArea);
1329 return STATUS_CONFLICTING_ADDRESSES;
1330 }
1331
1332 /* Get the ending address, which is the last piece we need for the VAD */
1333 EndingAddress = (StartAddress + *ViewSize - 1) | (PAGE_SIZE - 1);
1334 }
1335 else
1336 {
1337 /* Is it SEC_BASED, or did the caller manually specify an address? */
1338 if (!(*BaseAddress))
1339 {
1340 /* It is a SEC_BASED mapping, use the address that was generated */
1341 StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart;
1342 DPRINT("BASED: 0x%p\n", StartAddress);
1343 }
1344 else
1345 {
1346 /* Just align what the caller gave us */
1347 StartAddress = ROUND_UP((ULONG_PTR)*BaseAddress, _64K);
1348 }
1349
1350 /* Get the ending address, which is the last piece we need for the VAD */
1351 EndingAddress = (StartAddress + *ViewSize - 1) | (PAGE_SIZE - 1);
1352
1353 /* Make sure it doesn't conflict with an existing allocation */
1354 Result = MiCheckForConflictingNode(StartAddress >> PAGE_SHIFT,
1355 EndingAddress >> PAGE_SHIFT,
1356 &Process->VadRoot,
1357 &Parent);
1358 if (Result == TableFoundNode)
1359 {
1360 DPRINT1("Conflict with SEC_BASED or manually based section!\n");
1361 MiDereferenceControlArea(ControlArea);
1362 return STATUS_CONFLICTING_ADDRESSES;
1363 }
1364 }
1365
1366 /* A VAD can now be allocated. Do so and zero it out */
1367 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */
1368 ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */
1369 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
1370 if (!Vad)
1371 {
1372 MiDereferenceControlArea(ControlArea);
1373 return STATUS_INSUFFICIENT_RESOURCES;
1374 }
1375 RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
1376 Vad->u4.Banked = (PVOID)0xDEADBABE;
1377
1378 /* Write all the data required in the VAD for handling a fault */
1379 Vad->StartingVpn = StartAddress >> PAGE_SHIFT;
1380 Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
1381 Vad->ControlArea = ControlArea;
1382 Vad->u.VadFlags.Protection = ProtectionMask;
1383 Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16);
1384 Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
1385 if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange))
1386 {
1387 /* This isn't really implemented yet, but handle setting the flag */
1388 Vad->u.VadFlags.NoChange = 1;
1389 Vad->u2.VadFlags2.SecNoChange = 1;
1390 }
1391
1392 /* Finally, write down the first and last prototype PTE */
1393 Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
1394 PteOffset += (Vad->EndingVpn - Vad->StartingVpn);
1395 ASSERT(PteOffset < Subsection->PtesInSubsection);
1396 Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset];
1397
1398 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */
1399 ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
1400
1401 /* FIXME: Should setup VAD bitmap */
1402 Status = STATUS_SUCCESS;
1403
1404 /* Pretend as if we own the working set */
1405 MiLockProcessWorkingSetUnsafe(Process, Thread);
1406
1407 /* Insert the VAD */
1408 Process->VadRoot.NodeHint = Vad;
1409 MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
1410
1411 /* Release the working set */
1412 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
1413
1414 /* Windows stores this for accounting purposes, do so as well */
1415 if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress;
1416
1417 /* Check if anything was committed */
1418 if (QuotaCharge)
1419 {
1420 /* Set the start and end PTE addresses, and pick the template PTE */
1421 PointerPte = Vad->FirstPrototypePte;
1422 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
1423 TempPte = Segment->SegmentPteTemplate;
1424
1425 /* Acquire the commit lock and loop all prototype PTEs to be committed */
1426 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
1427 while (PointerPte < LastPte)
1428 {
1429 /* Make sure the PTE is already invalid */
1430 if (PointerPte->u.Long == 0)
1431 {
1432 /* And write the invalid PTE */
1433 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1434 }
1435 else
1436 {
1437 /* The PTE is valid, so skip it */
1438 QuotaExcess++;
1439 }
1440
1441 /* Move to the next PTE */
1442 PointerPte++;
1443 }
1444
1445 /* Now check how many pages exactly we committed, and update accounting */
1446 ASSERT(QuotaCharge >= QuotaExcess);
1447 QuotaCharge -= QuotaExcess;
1448 Segment->NumberOfCommittedPages += QuotaCharge;
1449 ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes);
1450
1451 /* Now that we're done, release the lock */
1452 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
1453 }
1454
1455 /* Finally, let the caller know where, and for what size, the view was mapped */
1456 *ViewSize = (ULONG_PTR)EndingAddress - (ULONG_PTR)StartAddress + 1;
1457 *BaseAddress = (PVOID)StartAddress;
1458 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize);
1459 return STATUS_SUCCESS;
1460 }
1461
1462 VOID
1463 NTAPI
1464 MiSubsectionConsistent(IN PSUBSECTION Subsection)
1465 {
1466 /* ReactOS only supports systems with 4K pages and 4K sectors */
1467 ASSERT(Subsection->u.SubsectionFlags.SectorEndOffset == 0);
1468
1469 /* Therefore, then number of PTEs should be equal to the number of sectors */
1470 if (Subsection->NumberOfFullSectors != Subsection->PtesInSubsection)
1471 {
1472 /* Break and warn if this is inconsistent */
1473 DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n",
1474 Subsection->NumberOfFullSectors, Subsection->PtesInSubsection);
1475 DbgBreakPoint();
1476 }
1477 }
1478
1479 NTSTATUS
1480 NTAPI
1481 MiCreateDataFileMap(IN PFILE_OBJECT File,
1482 OUT PSEGMENT *Segment,
1483 IN PSIZE_T MaximumSize,
1484 IN ULONG SectionPageProtection,
1485 IN ULONG AllocationAttributes,
1486 IN ULONG IgnoreFileSizing)
1487 {
1488 /* Not yet implemented */
1489 ASSERT(FALSE);
1490 *Segment = NULL;
1491 return STATUS_NOT_IMPLEMENTED;
1492 }
1493
1494 NTSTATUS
1495 NTAPI
1496 MiCreatePagingFileMap(OUT PSEGMENT *Segment,
1497 IN PSIZE_T MaximumSize,
1498 IN ULONG ProtectionMask,
1499 IN ULONG AllocationAttributes)
1500 {
1501 SIZE_T SizeLimit;
1502 PFN_COUNT PteCount;
1503 PMMPTE PointerPte;
1504 MMPTE TempPte;
1505 PCONTROL_AREA ControlArea;
1506 PSEGMENT NewSegment;
1507 PSUBSECTION Subsection;
1508 PAGED_CODE();
1509
1510 /* No large pages in ARM3 yet */
1511 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
1512
1513 /* Pagefile-backed sections need a known size */
1514 if (!(*MaximumSize)) return STATUS_INVALID_PARAMETER_4;
1515
1516 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */
1517 SizeLimit = MAXULONG_PTR - sizeof(SEGMENT);
1518 SizeLimit /= sizeof(MMPTE);
1519 SizeLimit <<= PAGE_SHIFT;
1520
1521 /* Fail if this size is too big */
1522 if (*MaximumSize > SizeLimit) return STATUS_SECTION_TOO_BIG;
1523
1524 /* Calculate how many Prototype PTEs will be needed */
1525 PteCount = (PFN_COUNT)((*MaximumSize + PAGE_SIZE - 1) >> PAGE_SHIFT);
1526
1527 /* For commited memory, we must have a valid protection mask */
1528 if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0);
1529
1530 /* The segment contains all the Prototype PTEs, allocate it in paged pool */
1531 NewSegment = ExAllocatePoolWithTag(PagedPool,
1532 sizeof(SEGMENT) +
1533 sizeof(MMPTE) * (PteCount - 1),
1534 'tSmM');
1535 ASSERT(NewSegment);
1536 *Segment = NewSegment;
1537
1538 /* Now allocate the control area, which has the subsection structure */
1539 ControlArea = ExAllocatePoolWithTag(NonPagedPool,
1540 sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
1541 'tCmM');
1542 ASSERT(ControlArea);
1543
1544 /* And zero it out, filling the basic segmnet pointer and reference fields */
1545 RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
1546 ControlArea->Segment = NewSegment;
1547 ControlArea->NumberOfSectionReferences = 1;
1548 ControlArea->NumberOfUserReferences = 1;
1549
1550 /* Convert allocation attributes to control area flags */
1551 if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1;
1552 if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1;
1553 if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1;
1554
1555 /* The subsection follows, write the mask, PTE count and point back to the CA */
1556 Subsection = (PSUBSECTION)(ControlArea + 1);
1557 Subsection->ControlArea = ControlArea;
1558 Subsection->PtesInSubsection = PteCount;
1559 Subsection->u.SubsectionFlags.Protection = ProtectionMask;
1560
1561 /* Zero out the segment's prototype PTEs, and link it with the control area */
1562 PointerPte = &NewSegment->ThePtes[0];
1563 RtlZeroMemory(NewSegment, sizeof(SEGMENT));
1564 NewSegment->PrototypePte = PointerPte;
1565 NewSegment->ControlArea = ControlArea;
1566
1567 /* Save some extra accounting data for the segment as well */
1568 NewSegment->u1.CreatingProcess = PsGetCurrentProcess();
1569 NewSegment->SizeOfSegment = PteCount * PAGE_SIZE;
1570 NewSegment->TotalNumberOfPtes = PteCount;
1571 NewSegment->NonExtendedPtes = PteCount;
1572
1573 /* The subsection's base address is the first Prototype PTE in the segment */
1574 Subsection->SubsectionBase = PointerPte;
1575
1576 /* Start with an empty PTE, unless this is a commit operation */
1577 TempPte.u.Long = 0;
1578 if (AllocationAttributes & SEC_COMMIT)
1579 {
1580 /* In which case, write down the protection mask in the Prototype PTEs */
1581 TempPte.u.Soft.Protection = ProtectionMask;
1582
1583 /* For accounting, also mark these pages as being committed */
1584 NewSegment->NumberOfCommittedPages = PteCount;
1585 }
1586
1587 /* The template PTE itself for the segment should also have the mask set */
1588 NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
1589
1590 /* Write out the prototype PTEs, for now they're simply demand zero */
1591 #ifdef _WIN64
1592 RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1593 #else
1594 RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1595 #endif
1596 return STATUS_SUCCESS;
1597 }
1598
1599 PFILE_OBJECT
1600 NTAPI
1601 MmGetFileObjectForSection(IN PVOID SectionObject)
1602 {
1603 PSECTION_OBJECT Section;
1604 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1605 ASSERT(SectionObject != NULL);
1606
1607 /* Check if it's an ARM3, or ReactOS section */
1608 if (MiIsRosSectionObject(SectionObject) == FALSE)
1609 {
1610 /* Return the file pointer stored in the control area */
1611 Section = SectionObject;
1612 return Section->Segment->ControlArea->FilePointer;
1613 }
1614
1615 /* Return the file object */
1616 return ((PROS_SECTION_OBJECT)SectionObject)->FileObject;
1617 }
1618
1619 VOID
1620 NTAPI
1621 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation)
1622 {
1623 PSECTION_OBJECT SectionObject;
1624
1625 /* Get the section object of this process*/
1626 SectionObject = PsGetCurrentProcess()->SectionObject;
1627 ASSERT(SectionObject != NULL);
1628 ASSERT(MiIsRosSectionObject(SectionObject) == TRUE);
1629
1630 /* Return the image information */
1631 DPRINT1("HERE!\n");
1632 *ImageInformation = ((PROS_SECTION_OBJECT)SectionObject)->ImageSection->ImageInformation;
1633 }
1634
1635 NTSTATUS
1636 NTAPI
1637 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject,
1638 OUT POBJECT_NAME_INFORMATION *ModuleName)
1639 {
1640 POBJECT_NAME_INFORMATION ObjectNameInfo;
1641 NTSTATUS Status;
1642 ULONG ReturnLength;
1643
1644 /* Allocate memory for our structure */
1645 ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, TAG_MM);
1646 if (!ObjectNameInfo) return STATUS_NO_MEMORY;
1647
1648 /* Query the name */
1649 Status = ObQueryNameString(FileObject,
1650 ObjectNameInfo,
1651 1024,
1652 &ReturnLength);
1653 if (!NT_SUCCESS(Status))
1654 {
1655 /* Failed, free memory */
1656 DPRINT1("Name query failed\n");
1657 ExFreePoolWithTag(ObjectNameInfo, TAG_MM);
1658 *ModuleName = NULL;
1659 return Status;
1660 }
1661
1662 /* Success */
1663 *ModuleName = ObjectNameInfo;
1664 return STATUS_SUCCESS;
1665 }
1666
1667 NTSTATUS
1668 NTAPI
1669 MmGetFileNameForSection(IN PVOID Section,
1670 OUT POBJECT_NAME_INFORMATION *ModuleName)
1671 {
1672 PFILE_OBJECT FileObject;
1673
1674 /* Make sure it's an image section */
1675 if (MiIsRosSectionObject(Section) == FALSE)
1676 {
1677 /* Check ARM3 Section flag */
1678 if (((PSECTION)Section)->u.Flags.Image == 0)
1679 {
1680 /* It's not, fail */
1681 DPRINT1("Not an image section\n");
1682 return STATUS_SECTION_NOT_IMAGE;
1683 }
1684 }
1685 else if (!(((PROS_SECTION_OBJECT)Section)->AllocationAttributes & SEC_IMAGE))
1686 {
1687 /* It's not, fail */
1688 DPRINT1("Not an image section\n");
1689 return STATUS_SECTION_NOT_IMAGE;
1690 }
1691
1692 /* Get the file object */
1693 FileObject = MmGetFileObjectForSection(Section);
1694 return MmGetFileNameForFileObject(FileObject, ModuleName);
1695 }
1696
1697 NTSTATUS
1698 NTAPI
1699 MmGetFileNameForAddress(IN PVOID Address,
1700 OUT PUNICODE_STRING ModuleName)
1701 {
1702 PVOID Section;
1703 PMEMORY_AREA MemoryArea;
1704 POBJECT_NAME_INFORMATION ModuleNameInformation;
1705 PVOID AddressSpace;
1706 NTSTATUS Status;
1707 PFILE_OBJECT FileObject = NULL;
1708 PMMVAD Vad;
1709 PCONTROL_AREA ControlArea;
1710
1711 /* Lock address space */
1712 AddressSpace = MmGetCurrentAddressSpace();
1713 MmLockAddressSpace(AddressSpace);
1714
1715 /* Locate the memory area for the process by address */
1716 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
1717 if (!MemoryArea)
1718 {
1719 /* Fail, the address does not exist */
1720 InvalidAddress:
1721 DPRINT1("Invalid address\n");
1722 MmUnlockAddressSpace(AddressSpace);
1723 return STATUS_INVALID_ADDRESS;
1724 }
1725
1726 /* Check if it's a section view (RosMm section) or ARM3 section */
1727 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1728 {
1729 /* Get the section pointer to the SECTION_OBJECT */
1730 Section = MemoryArea->Data.SectionData.Section;
1731
1732 /* Unlock address space */
1733 MmUnlockAddressSpace(AddressSpace);
1734
1735 /* Get the filename of the section */
1736 Status = MmGetFileNameForSection(Section, &ModuleNameInformation);
1737 }
1738 else if (MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3)
1739 {
1740 /* Get the VAD */
1741 Vad = MiLocateAddress(Address);
1742 if (!Vad) goto InvalidAddress;
1743
1744 /* Make sure it's not a VM VAD */
1745 if (Vad->u.VadFlags.PrivateMemory == 1)
1746 {
1747 NotSection:
1748 DPRINT1("Address is not a section\n");
1749 MmUnlockAddressSpace(AddressSpace);
1750 return STATUS_SECTION_NOT_IMAGE;
1751 }
1752
1753 /* Get the control area */
1754 ControlArea = Vad->ControlArea;
1755 if (!(ControlArea) || !(ControlArea->u.Flags.Image)) goto NotSection;
1756
1757 /* Get the file object */
1758 FileObject = ControlArea->FilePointer;
1759 ASSERT(FileObject != NULL);
1760 ObReferenceObject(FileObject);
1761
1762 /* Unlock address space */
1763 MmUnlockAddressSpace(AddressSpace);
1764
1765 /* Get the filename of the file object */
1766 Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation);
1767
1768 /* Dereference it */
1769 ObDereferenceObject(FileObject);
1770 }
1771 else
1772 {
1773 /* Trying to access virtual memory or something */
1774 goto InvalidAddress;
1775 }
1776
1777 /* Check if we were able to get the file object name */
1778 if (NT_SUCCESS(Status))
1779 {
1780 /* Init modulename */
1781 RtlCreateUnicodeString(ModuleName,
1782 ModuleNameInformation->Name.Buffer);
1783
1784 /* Free temp taged buffer from MmGetFileNameForFileObject() */
1785 ExFreePoolWithTag(ModuleNameInformation, TAG_MM);
1786 DPRINT("Found ModuleName %S by address %p\n", ModuleName->Buffer, Address);
1787 }
1788
1789 /* Return status */
1790 return Status;
1791 }
1792
1793 NTSTATUS
1794 NTAPI
1795 MiQueryMemorySectionName(IN HANDLE ProcessHandle,
1796 IN PVOID BaseAddress,
1797 OUT PVOID MemoryInformation,
1798 IN SIZE_T MemoryInformationLength,
1799 OUT PSIZE_T ReturnLength)
1800 {
1801 PEPROCESS Process;
1802 NTSTATUS Status;
1803 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1804 UNICODE_STRING ModuleFileName;
1805 PMEMORY_SECTION_NAME SectionName = NULL;
1806 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1807
1808 Status = ObReferenceObjectByHandle(ProcessHandle,
1809 PROCESS_QUERY_INFORMATION,
1810 NULL,
1811 PreviousMode,
1812 (PVOID*)(&Process),
1813 NULL);
1814
1815 if (!NT_SUCCESS(Status))
1816 {
1817 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
1818 return Status;
1819 }
1820
1821 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1822 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
1823
1824 if (NT_SUCCESS(Status))
1825 {
1826 SectionName = MemoryInformation;
1827 if (PreviousMode != KernelMode)
1828 {
1829 _SEH2_TRY
1830 {
1831 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1832 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1833 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1834
1835 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1836
1837 }
1838 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1839 {
1840 Status = _SEH2_GetExceptionCode();
1841 }
1842 _SEH2_END;
1843 }
1844 else
1845 {
1846 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1847 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1848 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1849
1850 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1851
1852 }
1853 }
1854 ObDereferenceObject(Process);
1855 return Status;
1856 }
1857
1858 VOID
1859 NTAPI
1860 MiFlushTbAndCapture(IN PMMVAD FoundVad,
1861 IN PMMPTE PointerPte,
1862 IN ULONG ProtectionMask,
1863 IN PMMPFN Pfn1,
1864 IN BOOLEAN CaptureDirtyBit)
1865 {
1866 MMPTE TempPte, PreviousPte;
1867 KIRQL OldIrql;
1868 BOOLEAN RebuildPte = FALSE;
1869
1870 //
1871 // User for sanity checking later on
1872 //
1873 PreviousPte = *PointerPte;
1874
1875 //
1876 // Build the PTE and acquire the PFN lock
1877 //
1878 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1879 PointerPte,
1880 ProtectionMask,
1881 PreviousPte.u.Hard.PageFrameNumber);
1882 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1883
1884 //
1885 // We don't support I/O mappings in this path yet
1886 //
1887 ASSERT(Pfn1 != NULL);
1888 ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined);
1889
1890 //
1891 // Make sure new protection mask doesn't get in conflict and fix it if it does
1892 //
1893 if (Pfn1->u3.e1.CacheAttribute == MiCached)
1894 {
1895 //
1896 // This is a cached PFN
1897 //
1898 if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS))
1899 {
1900 RebuildPte = TRUE;
1901 ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS);
1902 }
1903 }
1904 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
1905 {
1906 //
1907 // This is a non-cached PFN
1908 //
1909 if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE)
1910 {
1911 RebuildPte = TRUE;
1912 ProtectionMask &= ~MM_NOACCESS;
1913 ProtectionMask |= MM_NOCACHE;
1914 }
1915 }
1916
1917 if (RebuildPte)
1918 {
1919 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1920 PointerPte,
1921 ProtectionMask,
1922 PreviousPte.u.Hard.PageFrameNumber);
1923 }
1924
1925 //
1926 // Write the new PTE, making sure we are only changing the bits
1927 //
1928 MI_UPDATE_VALID_PTE(PointerPte, TempPte);
1929
1930 //
1931 // Flush the TLB
1932 //
1933 ASSERT(PreviousPte.u.Hard.Valid == 1);
1934 KeFlushCurrentTb();
1935 ASSERT(PreviousPte.u.Hard.Valid == 1);
1936
1937 //
1938 // Windows updates the relevant PFN1 information, we currently don't.
1939 //
1940 if (CaptureDirtyBit) DPRINT1("Warning, not handling dirty bit\n");
1941
1942 //
1943 // Not supported in ARM3
1944 //
1945 ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch);
1946
1947 //
1948 // Release the PFN lock, we are done
1949 //
1950 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1951 }
1952
1953 //
1954 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
1955 //
1956 NTSTATUS
1957 NTAPI
1958 MiSetProtectionOnSection(IN PEPROCESS Process,
1959 IN PMMVAD FoundVad,
1960 IN PVOID StartingAddress,
1961 IN PVOID EndingAddress,
1962 IN ULONG NewProtect,
1963 OUT PULONG CapturedOldProtect,
1964 IN ULONG DontCharge,
1965 OUT PULONG Locked)
1966 {
1967 PMMPTE PointerPte, LastPte;
1968 MMPTE TempPte, PteContents;
1969 PMMPDE PointerPde;
1970 PMMPFN Pfn1;
1971 ULONG ProtectionMask, QuotaCharge = 0;
1972 PETHREAD Thread = PsGetCurrentThread();
1973 PAGED_CODE();
1974
1975 //
1976 // Tell caller nothing is being locked
1977 //
1978 *Locked = FALSE;
1979
1980 //
1981 // This function should only be used for section VADs. Windows ASSERT */
1982 //
1983 ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0);
1984
1985 //
1986 // We don't support these features in ARM3
1987 //
1988 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
1989 ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0);
1990
1991 //
1992 // Convert and validate the protection mask
1993 //
1994 ProtectionMask = MiMakeProtectionMask(NewProtect);
1995 if (ProtectionMask == MM_INVALID_PROTECTION)
1996 {
1997 DPRINT1("Invalid section protect\n");
1998 return STATUS_INVALID_PAGE_PROTECTION;
1999 }
2000
2001 //
2002 // Get the PTE and PDE for the address, as well as the final PTE
2003 //
2004 MiLockProcessWorkingSetUnsafe(Process, Thread);
2005 PointerPde = MiAddressToPde(StartingAddress);
2006 PointerPte = MiAddressToPte(StartingAddress);
2007 LastPte = MiAddressToPte(EndingAddress);
2008
2009 //
2010 // Make the PDE valid, and check the status of the first PTE
2011 //
2012 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2013 if (PointerPte->u.Long)
2014 {
2015 //
2016 // Not supported in ARM3
2017 //
2018 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
2019
2020 //
2021 // Capture the page protection and make the PDE valid
2022 //
2023 *CapturedOldProtect = MiGetPageProtection(PointerPte);
2024 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2025 }
2026 else
2027 {
2028 //
2029 // Only pagefile-backed section VADs are supported for now
2030 //
2031 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2032
2033 //
2034 // Grab the old protection from the VAD itself
2035 //
2036 *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection];
2037 }
2038
2039 //
2040 // Loop all the PTEs now
2041 //
2042 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2043 while (PointerPte <= LastPte)
2044 {
2045 //
2046 // Check if we've crossed a PDE boundary and make the new PDE valid too
2047 //
2048 if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
2049 {
2050 PointerPde = MiAddressToPte(PointerPte);
2051 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2052 }
2053
2054 //
2055 // Capture the PTE and see what we're dealing with
2056 //
2057 PteContents = *PointerPte;
2058 if (PteContents.u.Long == 0)
2059 {
2060 //
2061 // This used to be a zero PTE and it no longer is, so we must add a
2062 // reference to the pagetable.
2063 //
2064 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2065
2066 //
2067 // Create the demand-zero prototype PTE
2068 //
2069 TempPte = PrototypePte;
2070 TempPte.u.Soft.Protection = ProtectionMask;
2071 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2072 }
2073 else if (PteContents.u.Hard.Valid == 1)
2074 {
2075 //
2076 // Get the PFN entry
2077 //
2078 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2079
2080 //
2081 // We don't support these yet
2082 //
2083 ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0);
2084 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2085
2086 //
2087 // Write the protection mask and write it with a TLB flush
2088 //
2089 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2090 MiFlushTbAndCapture(FoundVad,
2091 PointerPte,
2092 ProtectionMask,
2093 Pfn1,
2094 TRUE);
2095 }
2096 else
2097 {
2098 //
2099 // We don't support these cases yet
2100 //
2101 ASSERT(PteContents.u.Soft.Prototype == 0);
2102 ASSERT(PteContents.u.Soft.Transition == 0);
2103
2104 //
2105 // The PTE is already demand-zero, just update the protection mask
2106 //
2107 PointerPte->u.Soft.Protection = ProtectionMask;
2108 }
2109
2110 PointerPte++;
2111 }
2112
2113 //
2114 // Unlock the working set and update quota charges if needed, then return
2115 //
2116 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2117 if ((QuotaCharge > 0) && (!DontCharge))
2118 {
2119 FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
2120 Process->CommitCharge -= QuotaCharge;
2121 }
2122 return STATUS_SUCCESS;
2123 }
2124
2125 VOID
2126 NTAPI
2127 MiRemoveMappedPtes(IN PVOID BaseAddress,
2128 IN ULONG NumberOfPtes,
2129 IN PCONTROL_AREA ControlArea,
2130 IN PMMSUPPORT Ws)
2131 {
2132 PMMPTE PointerPte, ProtoPte;//, FirstPte;
2133 PMMPDE PointerPde, SystemMapPde;
2134 PMMPFN Pfn1, Pfn2;
2135 MMPTE PteContents;
2136 KIRQL OldIrql;
2137 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress);
2138
2139 ASSERT(Ws == NULL);
2140
2141 /* Get the PTE and loop each one */
2142 PointerPte = MiAddressToPte(BaseAddress);
2143 //FirstPte = PointerPte;
2144 while (NumberOfPtes)
2145 {
2146 /* Check if the PTE is already valid */
2147 PteContents = *PointerPte;
2148 if (PteContents.u.Hard.Valid == 1)
2149 {
2150 /* Get the PFN entry */
2151 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2152
2153 /* Get the PTE */
2154 PointerPde = MiAddressToPte(PointerPte);
2155
2156 /* Lock the PFN database and make sure this isn't a mapped file */
2157 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2158 ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0);
2159
2160 /* FIXME: Dirty bit management */
2161
2162 /* Was the PDE invalid */
2163 if (PointerPde->u.Long == 0)
2164 {
2165 #if (_MI_PAGING_LEVELS == 2)
2166 /* Find the system double-mapped PDE that describes this mapping */
2167 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
2168
2169 /* Make it valid */
2170 ASSERT(SystemMapPde->u.Hard.Valid == 1);
2171 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde);
2172 #else
2173 ASSERT(FALSE);
2174 #endif
2175 }
2176
2177 /* Dereference the PDE and the PTE */
2178 Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
2179 //MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2180 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
2181 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2182
2183 /* Release the PFN lock */
2184 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2185 }
2186 else
2187 {
2188 /* Windows ASSERT */
2189 ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1));
2190
2191 /* Check if this is a prototype pointer PTE */
2192 if (PteContents.u.Soft.Prototype == 1)
2193 {
2194 /* Get the prototype PTE */
2195 ProtoPte = MiProtoPteToPte(&PteContents);
2196
2197 /* We don't support anything else atm */
2198 ASSERT(ProtoPte->u.Long == 0);
2199 }
2200 }
2201
2202 /* Make the PTE into a zero PTE */
2203 PointerPte->u.Long = 0;
2204
2205 /* Move to the next PTE */
2206 PointerPte++;
2207 NumberOfPtes--;
2208 }
2209
2210 /* Flush the TLB */
2211 KeFlushCurrentTb();
2212
2213 /* Acquire the PFN lock */
2214 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2215
2216 /* Decrement the accounting counters */
2217 ControlArea->NumberOfUserReferences--;
2218 ControlArea->NumberOfMappedViews--;
2219
2220 /* Check if we should destroy the CA and release the lock */
2221 MiCheckControlArea(ControlArea, OldIrql);
2222 }
2223
2224 ULONG
2225 NTAPI
2226 MiRemoveFromSystemSpace(IN PMMSESSION Session,
2227 IN PVOID Base,
2228 OUT PCONTROL_AREA *ControlArea)
2229 {
2230 ULONG Hash, Size, Count = 0;
2231 ULONG_PTR Entry;
2232 PAGED_CODE();
2233
2234 /* Compute the hash for this entry and loop trying to find it */
2235 Entry = (ULONG_PTR)Base >> 16;
2236 Hash = Entry % Session->SystemSpaceHashKey;
2237 while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry)
2238 {
2239 /* Check if we overflew past the end of the hash table */
2240 if (++Hash >= Session->SystemSpaceHashSize)
2241 {
2242 /* Reset the hash to zero and keep searching from the bottom */
2243 Hash = 0;
2244 if (++Count == 2)
2245 {
2246 /* But if we overflew twice, then this is not a real mapping */
2247 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2248 (ULONG_PTR)Base,
2249 1,
2250 0,
2251 0);
2252 }
2253 }
2254 }
2255
2256 /* One less entry */
2257 Session->SystemSpaceHashEntries--;
2258
2259 /* Extract the size and clear the entry */
2260 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2261 Session->SystemSpaceViewTable[Hash].Entry = 0;
2262
2263 /* Return the control area and the size */
2264 *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2265 return Size;
2266 }
2267
2268 NTSTATUS
2269 NTAPI
2270 MiUnmapViewInSystemSpace(IN PMMSESSION Session,
2271 IN PVOID MappedBase)
2272 {
2273 ULONG Size;
2274 PCONTROL_AREA ControlArea;
2275 PAGED_CODE();
2276
2277 /* Remove this mapping */
2278 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2279 Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
2280
2281 /* Clear the bits for this mapping */
2282 RtlClearBits(Session->SystemSpaceBitMap,
2283 (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16),
2284 Size);
2285
2286 /* Convert the size from a bit size into the actual size */
2287 Size = Size * (_64K >> PAGE_SHIFT);
2288
2289 /* Remove the PTEs now */
2290 MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL);
2291 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2292
2293 /* Return success */
2294 return STATUS_SUCCESS;
2295 }
2296
2297 /* PUBLIC FUNCTIONS ***********************************************************/
2298
2299 /*
2300 * @implemented
2301 */
2302 NTSTATUS
2303 NTAPI
2304 MmCreateArm3Section(OUT PVOID *SectionObject,
2305 IN ACCESS_MASK DesiredAccess,
2306 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2307 IN PLARGE_INTEGER InputMaximumSize,
2308 IN ULONG SectionPageProtection,
2309 IN ULONG AllocationAttributes,
2310 IN HANDLE FileHandle OPTIONAL,
2311 IN PFILE_OBJECT FileObject OPTIONAL)
2312 {
2313 SECTION Section;
2314 PSECTION NewSection;
2315 PSUBSECTION Subsection;
2316 PSEGMENT NewSegment, Segment;
2317 NTSTATUS Status;
2318 PCONTROL_AREA ControlArea;
2319 ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge;
2320 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2321 BOOLEAN FileLock = FALSE, KernelCall = FALSE;
2322 KIRQL OldIrql;
2323 PFILE_OBJECT File;
2324 PVOID PreviousSectionPointer;
2325
2326 /* Make the same sanity checks that the Nt interface should've validated */
2327 ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
2328 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
2329 SEC_NO_CHANGE)) == 0);
2330 ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
2331 ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
2332 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
2333 SEC_NOCACHE | SEC_NO_CHANGE))));
2334 ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
2335 ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
2336 (SectionPageProtection & PAGE_WRITECOMBINE) ||
2337 (SectionPageProtection & PAGE_GUARD) ||
2338 (SectionPageProtection & PAGE_NOACCESS)));
2339
2340 /* Convert section flag to page flag */
2341 if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
2342
2343 /* Check to make sure the protection is correct. Nt* does this already */
2344 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
2345 if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
2346
2347 /* Check if this is going to be a data or image backed file section */
2348 if ((FileHandle) || (FileObject))
2349 {
2350 /* These cannot be mapped with large pages */
2351 if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6;
2352
2353 /* For now, only support the mechanism through a file handle */
2354 ASSERT(FileObject == NULL);
2355
2356 /* Reference the file handle to get the object */
2357 Status = ObReferenceObjectByHandle(FileHandle,
2358 MmMakeFileAccess[ProtectionMask],
2359 IoFileObjectType,
2360 PreviousMode,
2361 (PVOID*)&File,
2362 NULL);
2363 if (!NT_SUCCESS(Status)) return Status;
2364
2365 /* Make sure Cc has been doing its job */
2366 if (!File->SectionObjectPointer)
2367 {
2368 /* This is not a valid file system-based file, fail */
2369 ObDereferenceObject(File);
2370 return STATUS_INVALID_FILE_FOR_SECTION;
2371 }
2372
2373 /* Image-file backed sections are not yet supported */
2374 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2375
2376 /* Compute the size of the control area, and allocate it */
2377 ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
2378 ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM');
2379 if (!ControlArea)
2380 {
2381 ObDereferenceObject(File);
2382 return STATUS_INSUFFICIENT_RESOURCES;
2383 }
2384
2385 /* Zero it out */
2386 RtlZeroMemory(ControlArea, ControlAreaSize);
2387
2388 /* Did we get a handle, or an object? */
2389 if (FileHandle)
2390 {
2391 /* We got a file handle so we have to lock down the file */
2392 #if 0
2393 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection);
2394 if (!NT_SUCCESS(Status))
2395 {
2396 ExFreePool(ControlArea);
2397 ObDereferenceObject(File);
2398 return Status;
2399 }
2400 #else
2401 /* ReactOS doesn't support this API yet, so do nothing */
2402 Status = STATUS_SUCCESS;
2403 #endif
2404 /* Update the top-level IRP so that drivers know what's happening */
2405 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
2406 FileLock = TRUE;
2407 }
2408
2409 /* Lock the PFN database while we play with the section pointers */
2410 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2411
2412 /* Image-file backed sections are not yet supported */
2413 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2414
2415 /* There should not already be a control area for this file */
2416 ASSERT(File->SectionObjectPointer->DataSectionObject == NULL);
2417 NewSegment = NULL;
2418
2419 /* Write down that this CA is being created, and set it */
2420 ControlArea->u.Flags.BeingCreated = TRUE;
2421 PreviousSectionPointer = File->SectionObjectPointer;
2422 File->SectionObjectPointer->DataSectionObject = ControlArea;
2423
2424 /* We can release the PFN lock now */
2425 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2426
2427 /* We don't support previously-mapped file */
2428 ASSERT(NewSegment == NULL);
2429
2430 /* Image-file backed sections are not yet supported */
2431 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2432
2433 /* So we always create a data file map */
2434 Status = MiCreateDataFileMap(File,
2435 &Segment,
2436 (PSIZE_T)InputMaximumSize,
2437 SectionPageProtection,
2438 AllocationAttributes,
2439 KernelCall);
2440 ASSERT(PreviousSectionPointer == File->SectionObjectPointer);
2441 ASSERT(NT_SUCCESS(Status));
2442
2443 /* Check if a maximum size was specified */
2444 if (!InputMaximumSize->QuadPart)
2445 {
2446 /* Nope, use the segment size */
2447 Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
2448 }
2449 else
2450 {
2451 /* Yep, use the entered size */
2452 Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart;
2453 }
2454 }
2455 else
2456 {
2457 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2458 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
2459
2460 /* Not yet supported */
2461 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
2462
2463 /* So this must be a pagefile-backed section, create the mappings needed */
2464 Status = MiCreatePagingFileMap(&NewSegment,
2465 (PSIZE_T)InputMaximumSize,
2466 ProtectionMask,
2467 AllocationAttributes);
2468 if (!NT_SUCCESS(Status)) return Status;
2469
2470 /* Set the size here, and read the control area */
2471 Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
2472 ControlArea = NewSegment->ControlArea;
2473 }
2474
2475 /* Did we already have a segment? */
2476 if (!NewSegment)
2477 {
2478 /* This must be the file path and we created a segment */
2479 NewSegment = Segment;
2480 ASSERT(File != NULL);
2481
2482 /* Acquire the PFN lock while we set control area flags */
2483 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2484
2485 /* We don't support this race condition yet, so assume no waiters */
2486 ASSERT(ControlArea->WaitingForDeletion == NULL);
2487 ControlArea->WaitingForDeletion = NULL;
2488
2489 /* Image-file backed sections are not yet supported, nor ROM images */
2490 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2491 ASSERT(Segment->ControlArea->u.Flags.Rom == 0);
2492
2493 /* Take off the being created flag, and then release the lock */
2494 ControlArea->u.Flags.BeingCreated = FALSE;
2495 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2496 }
2497
2498 /* Check if we locked the file earlier */
2499 if (FileLock)
2500 {
2501 /* Reset the top-level IRP and release the lock */
2502 IoSetTopLevelIrp(NULL);
2503 //FsRtlReleaseFile(File);
2504 FileLock = FALSE;
2505 }
2506
2507 /* Set the initial section object data */
2508 Section.InitialPageProtection = SectionPageProtection;
2509
2510 /* The mapping created a control area and segment, save the flags */
2511 Section.Segment = NewSegment;
2512 Section.u.LongFlags = ControlArea->u.LongFlags;
2513
2514 /* Check if this is a user-mode read-write non-image file mapping */
2515 if (!(FileObject) &&
2516 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2517 (ControlArea->u.Flags.Image == 0) &&
2518 (ControlArea->FilePointer != NULL))
2519 {
2520 /* Add a reference and set the flag */
2521 Section.u.Flags.UserWritable = 1;
2522 InterlockedIncrement((PLONG)&ControlArea->WritableUserReferences);
2523 }
2524
2525 /* Check for image mappings or page file mappings */
2526 if ((ControlArea->u.Flags.Image == 1) || !(ControlArea->FilePointer))
2527 {
2528 /* Charge the segment size, and allocate a subsection */
2529 PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
2530 Size = sizeof(SUBSECTION);
2531 }
2532 else
2533 {
2534 /* Charge nothing, and allocate a mapped subsection */
2535 PagedCharge = 0;
2536 Size = sizeof(MSUBSECTION);
2537 }
2538
2539 /* Check if this is a normal CA */
2540 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2541 ASSERT(ControlArea->u.Flags.Rom == 0);
2542
2543 /* Charge only a CA, and the subsection is right after */
2544 NonPagedCharge = sizeof(CONTROL_AREA);
2545 Subsection = (PSUBSECTION)(ControlArea + 1);
2546
2547 /* We only support single-subsection mappings */
2548 NonPagedCharge += Size;
2549 ASSERT(Subsection->NextSubsection == NULL);
2550
2551 /* Create the actual section object, with enough space for the prototype PTEs */
2552 Status = ObCreateObject(PreviousMode,
2553 MmSectionObjectType,
2554 ObjectAttributes,
2555 PreviousMode,
2556 NULL,
2557 sizeof(SECTION),
2558 PagedCharge,
2559 NonPagedCharge,
2560 (PVOID*)&NewSection);
2561 ASSERT(NT_SUCCESS(Status));
2562
2563 /* Now copy the local section object from the stack into this new object */
2564 RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
2565 NewSection->Address.StartingVpn = 0;
2566
2567 /* For now, only user calls are supported */
2568 ASSERT(KernelCall == FALSE);
2569 NewSection->u.Flags.UserReference = TRUE;
2570
2571 /* Migrate the attribute into a flag */
2572 if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE;
2573
2574 /* If R/W access is not requested, this might eventually become a CoW mapping */
2575 if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
2576 {
2577 NewSection->u.Flags.CopyOnWrite = TRUE;
2578 }
2579
2580 /* Is this a "based" allocation, in which all mappings are identical? */
2581 if (AllocationAttributes & SEC_BASED)
2582 {
2583 /* Convert the flag, and make sure the section isn't too big */
2584 NewSection->u.Flags.Based = TRUE;
2585 if ((ULONGLONG)NewSection->SizeOfSection.QuadPart >
2586 (ULONG_PTR)MmHighSectionBase)
2587 {
2588 DPRINT1("BASED section is too large\n");
2589 ObDereferenceObject(NewSection);
2590 return STATUS_NO_MEMORY;
2591 }
2592
2593 /* Lock the VAD tree during the search */
2594 KeAcquireGuardedMutex(&MmSectionBasedMutex);
2595
2596 /* Find an address top-down */
2597 Status = MiFindEmptyAddressRangeDownBasedTree(NewSection->SizeOfSection.LowPart,
2598 (ULONG_PTR)MmHighSectionBase,
2599 _64K,
2600 &MmSectionBasedRoot,
2601 &NewSection->Address.StartingVpn);
2602 ASSERT(NT_SUCCESS(Status));
2603
2604 /* Compute the ending address and insert it into the VAD tree */
2605 NewSection->Address.EndingVpn = NewSection->Address.StartingVpn +
2606 NewSection->SizeOfSection.LowPart -
2607 1;
2608 MiInsertBasedSection(NewSection);
2609
2610 /* Finally release the lock */
2611 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2612 }
2613
2614 /* Write down if this was a kernel call */
2615 ControlArea->u.Flags.WasPurged |= KernelCall;
2616 ASSERT(ControlArea->u.Flags.WasPurged == FALSE);
2617
2618 /* Make sure the segment and the section are the same size, or the section is smaller */
2619 ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment);
2620
2621 /* Return the object and the creation status */
2622 *SectionObject = (PVOID)NewSection;
2623 return Status;
2624 }
2625
2626 /*
2627 * @implemented
2628 */
2629 NTSTATUS
2630 NTAPI
2631 MmMapViewOfArm3Section(IN PVOID SectionObject,
2632 IN PEPROCESS Process,
2633 IN OUT PVOID *BaseAddress,
2634 IN ULONG_PTR ZeroBits,
2635 IN SIZE_T CommitSize,
2636 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
2637 IN OUT PSIZE_T ViewSize,
2638 IN SECTION_INHERIT InheritDisposition,
2639 IN ULONG AllocationType,
2640 IN ULONG Protect)
2641 {
2642 KAPC_STATE ApcState;
2643 BOOLEAN Attached = FALSE;
2644 PSECTION Section;
2645 PCONTROL_AREA ControlArea;
2646 ULONG ProtectionMask;
2647 NTSTATUS Status;
2648 PAGED_CODE();
2649
2650 /* Get the segment and control area */
2651 Section = (PSECTION)SectionObject;
2652 ControlArea = Section->Segment->ControlArea;
2653
2654 /* These flags/states are not yet supported by ARM3 */
2655 ASSERT(Section->u.Flags.Image == 0);
2656 ASSERT(Section->u.Flags.NoCache == 0);
2657 ASSERT(Section->u.Flags.WriteCombined == 0);
2658 ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2659
2660 /* FIXME */
2661 if ((AllocationType & MEM_RESERVE) != 0)
2662 {
2663 DPRINT1("MmMapViewOfArm3Section called with MEM_RESERVE, this is not implemented yet!!!\n");
2664 return STATUS_NOT_IMPLEMENTED;
2665 }
2666
2667 /* Check if the mapping protection is compatible with the create */
2668 if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2669 {
2670 DPRINT1("Mapping protection is incompatible\n");
2671 return STATUS_SECTION_PROTECTION;
2672 }
2673
2674 /* Check if the offset and size would cause an overflow */
2675 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2676 (ULONG64)SectionOffset->QuadPart)
2677 {
2678 DPRINT1("Section offset overflows\n");
2679 return STATUS_INVALID_VIEW_SIZE;
2680 }
2681
2682 /* Check if the offset and size are bigger than the section itself */
2683 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2684 (ULONG64)Section->SizeOfSection.QuadPart)
2685 {
2686 DPRINT1("Section offset is larger than section\n");
2687 return STATUS_INVALID_VIEW_SIZE;
2688 }
2689
2690 /* Check if the caller did not specify a view size */
2691 if (!(*ViewSize))
2692 {
2693 /* Compute it for the caller */
2694 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
2695
2696 /* Check if it's larger than 4GB or overflows into kernel-mode */
2697 if ((*ViewSize > 0xFFFFFFFF) ||
2698 (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize))
2699 {
2700 DPRINT1("Section view won't fit\n");
2701 return STATUS_INVALID_VIEW_SIZE;
2702 }
2703 }
2704
2705 /* Check if the commit size is larger than the view size */
2706 if (CommitSize > *ViewSize)
2707 {
2708 DPRINT1("Attempting to commit more than the view itself\n");
2709 return STATUS_INVALID_PARAMETER_5;
2710 }
2711
2712 /* Check if the view size is larger than the section */
2713 if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2714 {
2715 DPRINT1("The view is larger than the section\n");
2716 return STATUS_INVALID_VIEW_SIZE;
2717 }
2718
2719 /* Compute and validate the protection mask */
2720 ProtectionMask = MiMakeProtectionMask(Protect);
2721 if (ProtectionMask == MM_INVALID_PROTECTION)
2722 {
2723 DPRINT1("The protection is invalid\n");
2724 return STATUS_INVALID_PAGE_PROTECTION;
2725 }
2726
2727 /* We only handle pagefile-backed sections, which cannot be writecombined */
2728 if (Protect & PAGE_WRITECOMBINE)
2729 {
2730 DPRINT1("Cannot write combine a pagefile-backed section\n");
2731 return STATUS_INVALID_PARAMETER_10;
2732 }
2733
2734 /* Start by attaching to the current process if needed */
2735 if (PsGetCurrentProcess() != Process)
2736 {
2737 KeStackAttachProcess(&Process->Pcb, &ApcState);
2738 Attached = TRUE;
2739 }
2740
2741 /* Lock the address space and make sure the process is alive */
2742 MmLockAddressSpace(&Process->Vm);
2743 if (!Process->VmDeleted)
2744 {
2745 /* Do the actual mapping */
2746 DPRINT("Mapping ARM3 data section\n");
2747 Status = MiMapViewOfDataSection(ControlArea,
2748 Process,
2749 BaseAddress,
2750 SectionOffset,
2751 ViewSize,
2752 Section,
2753 InheritDisposition,
2754 ProtectionMask,
2755 CommitSize,
2756 ZeroBits,
2757 AllocationType);
2758 }
2759 else
2760 {
2761 /* The process is being terminated, fail */
2762 DPRINT1("The process is dying\n");
2763 Status = STATUS_PROCESS_IS_TERMINATING;
2764 }
2765
2766 /* Unlock the address space and detatch if needed, then return status */
2767 MmUnlockAddressSpace(&Process->Vm);
2768 if (Attached) KeUnstackDetachProcess(&ApcState);
2769 return Status;
2770 }
2771
2772 /*
2773 * @unimplemented
2774 */
2775 BOOLEAN
2776 NTAPI
2777 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2778 {
2779 UNIMPLEMENTED;
2780 return FALSE;
2781 }
2782
2783 /*
2784 * @unimplemented
2785 */
2786 BOOLEAN
2787 NTAPI
2788 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2789 IN BOOLEAN DelayClose)
2790 {
2791 UNIMPLEMENTED;
2792 return FALSE;
2793 }
2794
2795 /*
2796 * @implemented
2797 */
2798 NTSTATUS
2799 NTAPI
2800 MmMapViewInSessionSpace(IN PVOID Section,
2801 OUT PVOID *MappedBase,
2802 IN OUT PSIZE_T ViewSize)
2803 {
2804 PAGED_CODE();
2805
2806 // HACK
2807 if (MiIsRosSectionObject(Section))
2808 {
2809 return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
2810 }
2811
2812 /* Process must be in a session */
2813 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2814 {
2815 DPRINT1("Process is not in session\n");
2816 return STATUS_NOT_MAPPED_VIEW;
2817 }
2818
2819 /* Use the system space API, but with the session view instead */
2820 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2821 return MiMapViewInSystemSpace(Section,
2822 &MmSessionSpace->Session,
2823 MappedBase,
2824 ViewSize);
2825 }
2826
2827 /*
2828 * @implemented
2829 */
2830 NTSTATUS
2831 NTAPI
2832 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
2833 {
2834 PAGED_CODE();
2835
2836 // HACK
2837 if (!MI_IS_SESSION_ADDRESS(MappedBase))
2838 {
2839 return MmUnmapViewInSystemSpace(MappedBase);
2840 }
2841
2842 /* Process must be in a session */
2843 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2844 {
2845 DPRINT1("Proess is not in session\n");
2846 return STATUS_NOT_MAPPED_VIEW;
2847 }
2848
2849 /* Use the system space API, but with the session view instead */
2850 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2851 return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
2852 MappedBase);
2853 }
2854
2855 /*
2856 * @implemented
2857 */
2858 NTSTATUS
2859 NTAPI
2860 MmUnmapViewOfSection(IN PEPROCESS Process,
2861 IN PVOID BaseAddress)
2862 {
2863 return MiUnmapViewOfSection(Process, BaseAddress, 0);
2864 }
2865
2866 /*
2867 * @implemented
2868 */
2869 NTSTATUS
2870 NTAPI
2871 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
2872 {
2873 PMEMORY_AREA MemoryArea;
2874 PAGED_CODE();
2875
2876 /* Was this mapped by RosMm? */
2877 MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
2878 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
2879 {
2880 return MiRosUnmapViewInSystemSpace(MappedBase);
2881 }
2882
2883 /* It was not, call the ARM3 routine */
2884 return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
2885 }
2886
2887 /*
2888 * @implemented
2889 */
2890 NTSTATUS
2891 NTAPI
2892 MmCommitSessionMappedView(IN PVOID MappedBase,
2893 IN SIZE_T ViewSize)
2894 {
2895 ULONG_PTR StartAddress, EndingAddress, Base;
2896 ULONG Hash, Count = 0, Size, QuotaCharge;
2897 PMMSESSION Session;
2898 PMMPTE LastProtoPte, PointerPte, ProtoPte;
2899 PCONTROL_AREA ControlArea;
2900 PSEGMENT Segment;
2901 PSUBSECTION Subsection;
2902 MMPTE TempPte;
2903 PAGED_CODE();
2904
2905 /* Make sure the base isn't past the session view range */
2906 if ((MappedBase < MiSessionViewStart) ||
2907 (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
2908 {
2909 DPRINT1("Base outside of valid range\n");
2910 return STATUS_INVALID_PARAMETER_1;
2911 }
2912
2913 /* Make sure the size isn't past the session view range */
2914 if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
2915 (ULONG_PTR)MappedBase) < ViewSize)
2916 {
2917 DPRINT1("Size outside of valid range\n");
2918 return STATUS_INVALID_PARAMETER_2;
2919 }
2920
2921 /* Sanity check */
2922 ASSERT(ViewSize != 0);
2923
2924 /* Process must be in a session */
2925 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2926 {
2927 DPRINT1("Process is not in session\n");
2928 return STATUS_NOT_MAPPED_VIEW;
2929 }
2930
2931 /* Compute the correctly aligned base and end addresses */
2932 StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
2933 EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
2934
2935 /* Sanity check and grab the session */
2936 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2937 Session = &MmSessionSpace->Session;
2938
2939 /* Get the hash entry for this allocation */
2940 Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
2941
2942 /* Lock system space */
2943 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2944
2945 /* Loop twice so we can try rolling over if needed */
2946 while (TRUE)
2947 {
2948 /* Extract the size and base addresses from the entry */
2949 Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
2950 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2951
2952 /* Convert the size to bucket chunks */
2953 Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
2954
2955 /* Bail out if this entry fits in here */
2956 if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
2957
2958 /* Check if we overflew past the end of the hash table */
2959 if (++Hash >= Session->SystemSpaceHashSize)
2960 {
2961 /* Reset the hash to zero and keep searching from the bottom */
2962 Hash = 0;
2963 if (++Count == 2)
2964 {
2965 /* But if we overflew twice, then this is not a real mapping */
2966 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2967 Base,
2968 2,
2969 0,
2970 0);
2971 }
2972 }
2973 }
2974
2975 /* Make sure the view being mapped is not file-based */
2976 ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2977 if (ControlArea->FilePointer != NULL)
2978 {
2979 /* It is, so we have to bail out */
2980 DPRINT1("Only page-filed backed sections can be commited\n");
2981 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2982 return STATUS_ALREADY_COMMITTED;
2983 }
2984
2985 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
2986 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2987 ASSERT(ControlArea->u.Flags.Rom == 0);
2988 Subsection = (PSUBSECTION)(ControlArea + 1);
2989
2990 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
2991 ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
2992 QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
2993 LastProtoPte = ProtoPte + QuotaCharge;
2994 if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
2995 {
2996 DPRINT1("PTE is out of bounds\n");
2997 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2998 return STATUS_INVALID_PARAMETER_2;
2999 }
3000
3001 /* Acquire the commit lock and count all the non-committed PTEs */
3002 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
3003 PointerPte = ProtoPte;
3004 while (PointerPte < LastProtoPte)
3005 {
3006 if (PointerPte->u.Long) QuotaCharge--;
3007 PointerPte++;
3008 }
3009
3010 /* Was everything committed already? */
3011 if (!QuotaCharge)
3012 {
3013 /* Nothing to do! */
3014 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3015 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3016 return STATUS_SUCCESS;
3017 }
3018
3019 /* Pick the segment and template PTE */
3020 Segment = ControlArea->Segment;
3021 TempPte = Segment->SegmentPteTemplate;
3022 ASSERT(TempPte.u.Long != 0);
3023
3024 /* Loop all prototype PTEs to be committed */
3025 PointerPte = ProtoPte;
3026 while (PointerPte < LastProtoPte)
3027 {
3028 /* Make sure the PTE is already invalid */
3029 if (PointerPte->u.Long == 0)
3030 {
3031 /* And write the invalid PTE */
3032 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3033 }
3034
3035 /* Move to the next PTE */
3036 PointerPte++;
3037 }
3038
3039 /* Check if we had at least one page charged */
3040 if (QuotaCharge)
3041 {
3042 /* Update the accounting data */
3043 Segment->NumberOfCommittedPages += QuotaCharge;
3044 InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3045 }
3046
3047 /* Release all */
3048 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3049 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3050 return STATUS_SUCCESS;
3051 }
3052
3053 /* SYSTEM CALLS ***************************************************************/
3054
3055 NTSTATUS
3056 NTAPI
3057 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3058 IN PVOID File2MappedAsFile)
3059 {
3060 PVOID AddressSpace;
3061 PMEMORY_AREA MemoryArea1, MemoryArea2;
3062 PROS_SECTION_OBJECT Section1, Section2;
3063
3064 /* Lock address space */
3065 AddressSpace = MmGetCurrentAddressSpace();
3066 MmLockAddressSpace(AddressSpace);
3067
3068 /* Locate the memory area for the process by address */
3069 MemoryArea1 = MmLocateMemoryAreaByAddress(AddressSpace, File1MappedAsAnImage);
3070 if (!MemoryArea1)
3071 {
3072 /* Fail, the address does not exist */
3073 MmUnlockAddressSpace(AddressSpace);
3074 return STATUS_INVALID_ADDRESS;
3075 }
3076
3077 /* Check if it's a section view (RosMm section) or ARM3 section */
3078 if (MemoryArea1->Type != MEMORY_AREA_SECTION_VIEW)
3079 {
3080 /* Fail, the address is not a section */
3081 MmUnlockAddressSpace(AddressSpace);
3082 return STATUS_CONFLICTING_ADDRESSES;
3083 }
3084
3085 /* Get the section pointer to the SECTION_OBJECT */
3086 Section1 = MemoryArea1->Data.SectionData.Section;
3087 if (Section1->FileObject == NULL)
3088 {
3089 MmUnlockAddressSpace(AddressSpace);
3090 return STATUS_CONFLICTING_ADDRESSES;
3091 }
3092
3093 /* Locate the memory area for the process by address */
3094 MemoryArea2 = MmLocateMemoryAreaByAddress(AddressSpace, File2MappedAsFile);
3095 if (!MemoryArea2)
3096 {
3097 /* Fail, the address does not exist */
3098 MmUnlockAddressSpace(AddressSpace);
3099 return STATUS_INVALID_ADDRESS;
3100 }
3101
3102 /* Check if it's a section view (RosMm section) or ARM3 section */
3103 if (MemoryArea2->Type != MEMORY_AREA_SECTION_VIEW)
3104 {
3105 /* Fail, the address is not a section */
3106 MmUnlockAddressSpace(AddressSpace);
3107 return STATUS_CONFLICTING_ADDRESSES;
3108 }
3109
3110 /* Get the section pointer to the SECTION_OBJECT */
3111 Section2 = MemoryArea2->Data.SectionData.Section;
3112 if (Section2->FileObject == NULL)
3113 {
3114 MmUnlockAddressSpace(AddressSpace);
3115 return STATUS_CONFLICTING_ADDRESSES;
3116 }
3117
3118 /* The shared cache map seems to be the same if both of these are equal */
3119 if (Section1->FileObject->SectionObjectPointer->SharedCacheMap ==
3120 Section2->FileObject->SectionObjectPointer->SharedCacheMap)
3121 {
3122 MmUnlockAddressSpace(AddressSpace);
3123 return STATUS_SUCCESS;
3124 }
3125
3126 /* Unlock address space */
3127 MmUnlockAddressSpace(AddressSpace);
3128 return STATUS_NOT_SAME_DEVICE;
3129 }
3130
3131 /*
3132 * @implemented
3133 */
3134 NTSTATUS
3135 NTAPI
3136 NtCreateSection(OUT PHANDLE SectionHandle,
3137 IN ACCESS_MASK DesiredAccess,
3138 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3139 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3140 IN ULONG SectionPageProtection OPTIONAL,
3141 IN ULONG AllocationAttributes,
3142 IN HANDLE FileHandle OPTIONAL)
3143 {
3144 LARGE_INTEGER SafeMaximumSize;
3145 PVOID SectionObject;
3146 HANDLE Handle;
3147 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3148 NTSTATUS Status;
3149 PAGED_CODE();
3150
3151 /* Check for non-existing flags */
3152 if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
3153 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
3154 SEC_NO_CHANGE)))
3155 {
3156 if (!(AllocationAttributes & 1))
3157 {
3158 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
3159 return STATUS_INVALID_PARAMETER_6;
3160 }
3161 }
3162
3163 /* Check for no allocation type */
3164 if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
3165 {
3166 DPRINT1("Missing allocation type in allocation attributes\n");
3167 return STATUS_INVALID_PARAMETER_6;
3168 }
3169
3170 /* Check for image allocation with invalid attributes */
3171 if ((AllocationAttributes & SEC_IMAGE) &&
3172 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
3173 SEC_NOCACHE | SEC_NO_CHANGE)))
3174 {
3175 DPRINT1("Image allocation with invalid attributes\n");
3176 return STATUS_INVALID_PARAMETER_6;
3177 }
3178
3179 /* Check for allocation type is both commit and reserve */
3180 if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
3181 {
3182 DPRINT1("Commit and reserve in the same time\n");
3183 return STATUS_INVALID_PARAMETER_6;
3184 }
3185
3186 /* Now check for valid protection */
3187 if ((SectionPageProtection & PAGE_NOCACHE) ||
3188 (SectionPageProtection & PAGE_WRITECOMBINE) ||
3189 (SectionPageProtection & PAGE_GUARD) ||
3190 (SectionPageProtection & PAGE_NOACCESS))
3191 {
3192 DPRINT1("Sections don't support these protections\n");
3193 return STATUS_INVALID_PAGE_PROTECTION;
3194 }
3195
3196 /* Use a maximum size of zero, if none was specified */
3197 SafeMaximumSize.QuadPart = 0;
3198
3199 /* Check for user-mode caller */
3200 if (PreviousMode != KernelMode)
3201 {
3202 /* Enter SEH */
3203 _SEH2_TRY
3204 {
3205 /* Safely check user-mode parameters */
3206 if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3207 MaximumSize = &SafeMaximumSize;
3208 ProbeForWriteHandle(SectionHandle);
3209 }
3210 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3211 {
3212 /* Return the exception code */
3213 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3214 }
3215 _SEH2_END;
3216 }
3217 else if (!MaximumSize) MaximumSize = &SafeMaximumSize;
3218
3219 /* Check that MaximumSize is valid if backed by paging file */
3220 if ((!FileHandle) && (!MaximumSize->QuadPart))
3221 return STATUS_INVALID_PARAMETER_4;
3222
3223 /* Create the section */
3224 Status = MmCreateSection(&SectionObject,
3225 DesiredAccess,
3226 ObjectAttributes,
3227 MaximumSize,
3228 SectionPageProtection,
3229 AllocationAttributes,
3230 FileHandle,
3231 NULL);
3232 if (!NT_SUCCESS(Status)) return Status;
3233
3234 /* FIXME: Should zero last page for a file mapping */
3235
3236 /* Now insert the object */
3237 Status = ObInsertObject(SectionObject,
3238 NULL,
3239 DesiredAccess,
3240 0,
3241 NULL,
3242 &Handle);
3243 if (NT_SUCCESS(Status))
3244 {
3245 /* Enter SEH */
3246 _SEH2_TRY
3247 {
3248 /* Return the handle safely */
3249 *SectionHandle = Handle;
3250 }
3251 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3252 {
3253 /* Nothing here */
3254 }
3255 _SEH2_END;
3256 }
3257
3258 /* Return the status */
3259 return Status;
3260 }
3261
3262 NTSTATUS
3263 NTAPI
3264 NtOpenSection(OUT PHANDLE SectionHandle,
3265 IN ACCESS_MASK DesiredAccess,
3266 IN POBJECT_ATTRIBUTES ObjectAttributes)
3267 {
3268 HANDLE Handle;
3269 NTSTATUS Status;
3270 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3271 PAGED_CODE();
3272
3273 /* Check for user-mode caller */
3274 if (PreviousMode != KernelMode)
3275 {
3276 /* Enter SEH */
3277 _SEH2_TRY
3278 {
3279 /* Safely check user-mode parameters */
3280 ProbeForWriteHandle(SectionHandle);
3281 }
3282 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3283 {
3284 /* Return the exception code */
3285 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3286 }
3287 _SEH2_END;
3288 }
3289
3290 /* Try opening the object */
3291 Status = ObOpenObjectByName(ObjectAttributes,
3292 MmSectionObjectType,
3293 PreviousMode,
3294 NULL,
3295 DesiredAccess,
3296 NULL,
3297 &Handle);
3298
3299 /* Enter SEH */
3300 _SEH2_TRY
3301 {
3302 /* Return the handle safely */
3303 *SectionHandle = Handle;
3304 }
3305 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3306 {
3307 /* Nothing here */
3308 }
3309 _SEH2_END;
3310
3311 /* Return the status */
3312 return Status;
3313 }
3314
3315 NTSTATUS
3316 NTAPI
3317 NtMapViewOfSection(IN HANDLE SectionHandle,
3318 IN HANDLE ProcessHandle,
3319 IN OUT PVOID* BaseAddress,
3320 IN ULONG_PTR ZeroBits,
3321 IN SIZE_T CommitSize,
3322 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3323 IN OUT PSIZE_T ViewSize,
3324 IN SECTION_INHERIT InheritDisposition,
3325 IN ULONG AllocationType,
3326 IN ULONG Protect)
3327 {
3328 PVOID SafeBaseAddress;
3329 LARGE_INTEGER SafeSectionOffset;
3330 SIZE_T SafeViewSize;
3331 PROS_SECTION_OBJECT Section;
3332 PEPROCESS Process;
3333 NTSTATUS Status;
3334 ACCESS_MASK DesiredAccess;
3335 ULONG ProtectionMask;
3336 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3337
3338 /* Check for invalid zero bits */
3339 if (ZeroBits > 21) // per-arch?
3340 {
3341 DPRINT1("Invalid zero bits\n");
3342 return STATUS_INVALID_PARAMETER_4;
3343 }
3344
3345 /* Check for invalid inherit disposition */
3346 if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare))
3347 {
3348 DPRINT1("Invalid inherit disposition\n");
3349 return STATUS_INVALID_PARAMETER_8;
3350 }
3351
3352 /* Allow only valid allocation types */
3353 if ((AllocationType & ~(MEM_TOP_DOWN | MEM_LARGE_PAGES | MEM_DOS_LIM |
3354 SEC_NO_CHANGE | MEM_RESERVE)))
3355 {
3356 DPRINT1("Invalid allocation type\n");
3357 return STATUS_INVALID_PARAMETER_9;
3358 }
3359
3360 /* Convert the protection mask, and validate it */
3361 ProtectionMask = MiMakeProtectionMask(Protect);
3362 if (ProtectionMask == MM_INVALID_PROTECTION)
3363 {
3364 DPRINT1("Invalid page protection\n");
3365 return STATUS_INVALID_PAGE_PROTECTION;
3366 }
3367
3368 /* Check for non-allocation-granularity-aligned BaseAddress */
3369 if (BaseAddress && (*BaseAddress != ALIGN_DOWN_POINTER_BY(*BaseAddress, MM_VIRTMEM_GRANULARITY)))
3370 {
3371 DPRINT("BaseAddress is not at 64-kilobyte address boundary.");
3372 return STATUS_MAPPED_ALIGNMENT;
3373 }
3374
3375 /* Now convert the protection mask into desired section access mask */
3376 DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
3377
3378 /* Assume no section offset */
3379 SafeSectionOffset.QuadPart = 0;
3380
3381 /* Enter SEH */
3382 _SEH2_TRY
3383 {
3384 /* Check for unsafe parameters */
3385 if (PreviousMode != KernelMode)
3386 {
3387 /* Probe the parameters */
3388 ProbeForWritePointer(BaseAddress);
3389 ProbeForWriteSize_t(ViewSize);
3390 }
3391
3392 /* Check if a section offset was given */
3393 if (SectionOffset)
3394 {
3395 /* Check for unsafe parameters and capture section offset */
3396 if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset);
3397 SafeSectionOffset = *SectionOffset;
3398 }
3399
3400 /* Capture the other parameters */
3401 SafeBaseAddress = *BaseAddress;
3402 SafeViewSize = *ViewSize;
3403 }
3404 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3405 {
3406 /* Return the exception code */
3407 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3408 }
3409 _SEH2_END;
3410
3411 /* Check for kernel-mode address */
3412 if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS)
3413 {
3414 DPRINT1("Kernel base not allowed\n");
3415 return STATUS_INVALID_PARAMETER_3;
3416 }
3417
3418 /* Check for range entering kernel-mode */
3419 if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize)
3420 {
3421 DPRINT1("Overflowing into kernel base not allowed\n");
3422 return STATUS_INVALID_PARAMETER_3;
3423 }
3424
3425 /* Check for invalid zero bits */
3426 if (((ULONG_PTR)SafeBaseAddress + SafeViewSize) > (0xFFFFFFFF >> ZeroBits)) // arch?
3427 {
3428 DPRINT1("Invalid zero bits\n");
3429 return STATUS_INVALID_PARAMETER_4;
3430 }
3431
3432 /* Reference the process */
3433 Status = ObReferenceObjectByHandle(ProcessHandle,
3434 PROCESS_VM_OPERATION,
3435 PsProcessType,
3436 PreviousMode,
3437 (PVOID*)&Process,
3438 NULL);
3439 if (!NT_SUCCESS(Status)) return Status;
3440
3441 /* Reference the section */
3442 Status = ObReferenceObjectByHandle(SectionHandle,
3443 DesiredAccess,
3444 MmSectionObjectType,
3445 PreviousMode,
3446 (PVOID*)&Section,
3447 NULL);
3448 if (!NT_SUCCESS(Status))
3449 {
3450 ObDereferenceObject(Process);
3451 return Status;
3452 }
3453
3454 /* Now do the actual mapping */
3455 Status = MmMapViewOfSection(Section,
3456 Process,
3457 &SafeBaseAddress,
3458 ZeroBits,
3459 CommitSize,
3460 &SafeSectionOffset,
3461 &SafeViewSize,
3462 InheritDisposition,
3463 AllocationType,
3464 Protect);
3465
3466 /* Return data only on success */
3467 if (NT_SUCCESS(Status))
3468 {
3469 /* Check if this is an image for the current process */
3470 if ((Section->AllocationAttributes & SEC_IMAGE) &&
3471 (Process == PsGetCurrentProcess()) &&
3472 (Status != STATUS_IMAGE_NOT_AT_BASE))
3473 {
3474 /* Notify the debugger */
3475 DbgkMapViewOfSection(Section,
3476 SafeBaseAddress,
3477 SafeSectionOffset.LowPart,
3478 SafeViewSize);
3479 }
3480
3481 /* Enter SEH */
3482 _SEH2_TRY
3483 {
3484 /* Return parameters to user */
3485 *BaseAddress = SafeBaseAddress;
3486 *ViewSize = SafeViewSize;
3487 if (SectionOffset) *SectionOffset = SafeSectionOffset;
3488 }
3489 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3490 {
3491 /* Nothing to do */
3492 }
3493 _SEH2_END;
3494 }
3495
3496 /* Dereference all objects and return status */
3497 ObDereferenceObject(Section);
3498 ObDereferenceObject(Process);
3499 return Status;
3500 }
3501
3502 NTSTATUS
3503 NTAPI
3504 NtUnmapViewOfSection(IN HANDLE ProcessHandle,
3505 IN PVOID BaseAddress)
3506 {
3507 PEPROCESS Process;
3508 NTSTATUS Status;
3509 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3510
3511 /* Don't allowing mapping kernel views */
3512 if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS))
3513 {
3514 DPRINT1("Trying to unmap a kernel view\n");
3515 return STATUS_NOT_MAPPED_VIEW;
3516 }
3517
3518 /* Reference the process */
3519 Status = ObReferenceObjectByHandle(ProcessHandle,
3520 PROCESS_VM_OPERATION,
3521 PsProcessType,
3522 PreviousMode,
3523 (PVOID*)&Process,
3524 NULL);
3525 if (!NT_SUCCESS(Status)) return Status;
3526
3527 /* Unmap the view */
3528 Status = MiUnmapViewOfSection(Process, BaseAddress, 0);
3529
3530 /* Dereference the process and return status */
3531 ObDereferenceObject(Process);
3532 return Status;
3533 }
3534
3535 NTSTATUS
3536 NTAPI
3537 NtExtendSection(IN HANDLE SectionHandle,
3538 IN OUT PLARGE_INTEGER NewMaximumSize)
3539 {
3540 LARGE_INTEGER SafeNewMaximumSize;
3541 PROS_SECTION_OBJECT Section;
3542 NTSTATUS Status;
3543 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3544
3545 /* Check for user-mode parameters */
3546 if (PreviousMode != KernelMode)
3547 {
3548 /* Enter SEH */
3549 _SEH2_TRY
3550 {
3551 /* Probe and capture the maximum size, it's both read and write */
3552 ProbeForWriteLargeInteger(NewMaximumSize);
3553 SafeNewMaximumSize = *NewMaximumSize;
3554 }
3555 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3556 {
3557 /* Return the exception code */
3558 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3559 }
3560 _SEH2_END;
3561 }
3562 else
3563 {
3564 /* Just read the size directly */
3565 SafeNewMaximumSize = *NewMaximumSize;
3566 }
3567
3568 /* Reference the section */
3569 Status = ObReferenceObjectByHandle(SectionHandle,
3570 SECTION_EXTEND_SIZE,
3571 MmSectionObjectType,
3572 PreviousMode,
3573 (PVOID*)&Section,
3574 NULL);
3575 if (!NT_SUCCESS(Status)) return Status;
3576
3577 /* Really this should go in MmExtendSection */
3578 if (!(Section->AllocationAttributes & SEC_FILE))
3579 {
3580 DPRINT1("Not extending a file\n");
3581 ObDereferenceObject(Section);
3582 return STATUS_SECTION_NOT_EXTENDED;
3583 }
3584
3585 /* FIXME: Do the work */
3586
3587 /* Dereference the section */
3588 ObDereferenceObject(Section);
3589
3590 /* Enter SEH */
3591 _SEH2_TRY
3592 {
3593 /* Write back the new size */
3594 *NewMaximumSize = SafeNewMaximumSize;
3595 }
3596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3597 {
3598 /* Nothing to do */
3599 }
3600 _SEH2_END;
3601
3602 /* Return the status */
3603 return STATUS_NOT_IMPLEMENTED;
3604 }
3605
3606 /* EOF */