d92a4adc5c0c9d45c2558c1e0daaaa28e7312ced
[reactos.git] / reactos / ntoskrnl / mm / ARM3 / section.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/section.c
5 * PURPOSE: ARM Memory Manager Section Support
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 #define MODULE_INVOLVED_IN_ARM3
16 #include "../ARM3/miarm.h"
17
18 /* GLOBALS ********************************************************************/
19
20 ACCESS_MASK MmMakeSectionAccess[8] =
21 {
22 SECTION_MAP_READ,
23 SECTION_MAP_READ,
24 SECTION_MAP_EXECUTE,
25 SECTION_MAP_EXECUTE | SECTION_MAP_READ,
26 SECTION_MAP_WRITE,
27 SECTION_MAP_READ,
28 SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
29 SECTION_MAP_EXECUTE | SECTION_MAP_READ
30 };
31
32 ACCESS_MASK MmMakeFileAccess[8] =
33 {
34 FILE_READ_DATA,
35 FILE_READ_DATA,
36 FILE_EXECUTE,
37 FILE_EXECUTE | FILE_READ_DATA,
38 FILE_WRITE_DATA | FILE_READ_DATA,
39 FILE_READ_DATA,
40 FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA,
41 FILE_EXECUTE | FILE_READ_DATA
42 };
43
44 CHAR MmUserProtectionToMask1[16] =
45 {
46 0,
47 MM_NOACCESS,
48 MM_READONLY,
49 (CHAR)MM_INVALID_PROTECTION,
50 MM_READWRITE,
51 (CHAR)MM_INVALID_PROTECTION,
52 (CHAR)MM_INVALID_PROTECTION,
53 (CHAR)MM_INVALID_PROTECTION,
54 MM_WRITECOPY,
55 (CHAR)MM_INVALID_PROTECTION,
56 (CHAR)MM_INVALID_PROTECTION,
57 (CHAR)MM_INVALID_PROTECTION,
58 (CHAR)MM_INVALID_PROTECTION,
59 (CHAR)MM_INVALID_PROTECTION,
60 (CHAR)MM_INVALID_PROTECTION,
61 (CHAR)MM_INVALID_PROTECTION
62 };
63
64 CHAR MmUserProtectionToMask2[16] =
65 {
66 0,
67 MM_EXECUTE,
68 MM_EXECUTE_READ,
69 (CHAR)MM_INVALID_PROTECTION,
70 MM_EXECUTE_READWRITE,
71 (CHAR)MM_INVALID_PROTECTION,
72 (CHAR)MM_INVALID_PROTECTION,
73 (CHAR)MM_INVALID_PROTECTION,
74 MM_EXECUTE_WRITECOPY,
75 (CHAR)MM_INVALID_PROTECTION,
76 (CHAR)MM_INVALID_PROTECTION,
77 (CHAR)MM_INVALID_PROTECTION,
78 (CHAR)MM_INVALID_PROTECTION,
79 (CHAR)MM_INVALID_PROTECTION,
80 (CHAR)MM_INVALID_PROTECTION,
81 (CHAR)MM_INVALID_PROTECTION
82 };
83
84 ULONG MmCompatibleProtectionMask[8] =
85 {
86 PAGE_NOACCESS,
87
88 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
89
90 PAGE_NOACCESS | PAGE_EXECUTE,
91
92 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
93 PAGE_EXECUTE_READ,
94
95 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE,
96
97 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY,
98
99 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_READWRITE |
100 PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE |
101 PAGE_EXECUTE_WRITECOPY,
102
103 PAGE_NOACCESS | PAGE_READONLY | PAGE_WRITECOPY | PAGE_EXECUTE |
104 PAGE_EXECUTE_READ | PAGE_EXECUTE_WRITECOPY
105 };
106
107 MMSESSION MmSession;
108 KGUARDED_MUTEX MmSectionCommitMutex;
109 MM_AVL_TABLE MmSectionBasedRoot;
110 KGUARDED_MUTEX MmSectionBasedMutex;
111 PVOID MmHighSectionBase;
112
113 /* PRIVATE FUNCTIONS **********************************************************/
114
115 BOOLEAN
116 NTAPI
117 MiIsProtectionCompatible(IN ULONG SectionPageProtection,
118 IN ULONG NewSectionPageProtection)
119 {
120 ULONG ProtectionMask, CompatibleMask;
121
122 /* Calculate the protection mask and make sure it's valid */
123 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
124 if (ProtectionMask == MM_INVALID_PROTECTION)
125 {
126 DPRINT1("Invalid protection mask\n");
127 return FALSE;
128 }
129
130 /* Calculate the compatible mask */
131 CompatibleMask = MmCompatibleProtectionMask[ProtectionMask & 0x7] |
132 PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE;
133
134 /* See if the mapping protection is compatible with the create protection */
135 return ((CompatibleMask | NewSectionPageProtection) == CompatibleMask);
136 }
137
138 ACCESS_MASK
139 NTAPI
140 MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection)
141 {
142 ULONG ProtectionMask;
143
144 /* Calculate the protection mask and make sure it's valid */
145 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
146 if (ProtectionMask == MM_INVALID_PROTECTION)
147 {
148 DPRINT1("Invalid protection mask\n");
149 return STATUS_INVALID_PAGE_PROTECTION;
150 }
151
152 /* Now convert it to the required file access */
153 return MmMakeFileAccess[ProtectionMask & 0x7];
154 }
155
156 ULONG
157 NTAPI
158 MiMakeProtectionMask(IN ULONG Protect)
159 {
160 ULONG Mask1, Mask2, ProtectMask;
161
162 /* PAGE_EXECUTE_WRITECOMBINE is theoretically the maximum */
163 if (Protect >= (PAGE_WRITECOMBINE * 2)) return MM_INVALID_PROTECTION;
164
165 /*
166 * Windows API protection mask can be understood as two bitfields, differing
167 * by whether or not execute rights are being requested
168 */
169 Mask1 = Protect & 0xF;
170 Mask2 = (Protect >> 4) & 0xF;
171
172 /* Check which field is there */
173 if (!Mask1)
174 {
175 /* Mask2 must be there, use it to determine the PTE protection */
176 if (!Mask2) return MM_INVALID_PROTECTION;
177 ProtectMask = MmUserProtectionToMask2[Mask2];
178 }
179 else
180 {
181 /* Mask2 should not be there, use Mask1 to determine the PTE mask */
182 if (Mask2) return MM_INVALID_PROTECTION;
183 ProtectMask = MmUserProtectionToMask1[Mask1];
184 }
185
186 /* Make sure the final mask is a valid one */
187 if (ProtectMask == MM_INVALID_PROTECTION) return MM_INVALID_PROTECTION;
188
189 /* Check for PAGE_GUARD option */
190 if (Protect & PAGE_GUARD)
191 {
192 /* It's not valid on no-access, nocache, or writecombine pages */
193 if ((ProtectMask == MM_NOACCESS) ||
194 (Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)))
195 {
196 /* Fail such requests */
197 return MM_INVALID_PROTECTION;
198 }
199
200 /* This actually turns on guard page in this scenario! */
201 ProtectMask |= MM_GUARDPAGE;
202 }
203
204 /* Check for nocache option */
205 if (Protect & PAGE_NOCACHE)
206 {
207 /* The earlier check should've eliminated this possibility */
208 ASSERT((Protect & PAGE_GUARD) == 0);
209
210 /* Check for no-access page or write combine page */
211 if ((ProtectMask == MM_NOACCESS) || (Protect & PAGE_WRITECOMBINE))
212 {
213 /* Such a request is invalid */
214 return MM_INVALID_PROTECTION;
215 }
216
217 /* Add the PTE flag */
218 ProtectMask |= MM_NOCACHE;
219 }
220
221 /* Check for write combine option */
222 if (Protect & PAGE_WRITECOMBINE)
223 {
224 /* The two earlier scenarios should've caught this */
225 ASSERT((Protect & (PAGE_GUARD | PAGE_NOACCESS)) == 0);
226
227 /* Don't allow on no-access pages */
228 if (ProtectMask == MM_NOACCESS) return MM_INVALID_PROTECTION;
229
230 /* This actually turns on write-combine in this scenario! */
231 ProtectMask |= MM_NOACCESS;
232 }
233
234 /* Return the final MM PTE protection mask */
235 return ProtectMask;
236 }
237
238 BOOLEAN
239 NTAPI
240 MiInitializeSystemSpaceMap(IN PMMSESSION InputSession OPTIONAL)
241 {
242 SIZE_T AllocSize, BitmapSize, Size;
243 PVOID ViewStart;
244 PMMSESSION Session;
245
246 /* Check if this a session or system space */
247 if (InputSession)
248 {
249 /* Use the input session */
250 Session = InputSession;
251 ViewStart = MiSessionViewStart;
252 Size = MmSessionViewSize;
253 }
254 else
255 {
256 /* Use the system space "session" */
257 Session = &MmSession;
258 ViewStart = MiSystemViewStart;
259 Size = MmSystemViewSize;
260 }
261
262 /* Initialize the system space lock */
263 Session->SystemSpaceViewLockPointer = &Session->SystemSpaceViewLock;
264 KeInitializeGuardedMutex(Session->SystemSpaceViewLockPointer);
265
266 /* Set the start address */
267 Session->SystemSpaceViewStart = ViewStart;
268
269 /* Create a bitmap to describe system space */
270 BitmapSize = sizeof(RTL_BITMAP) + ((((Size / MI_SYSTEM_VIEW_BUCKET_SIZE) + 31) / 32) * sizeof(ULONG));
271 Session->SystemSpaceBitMap = ExAllocatePoolWithTag(NonPagedPool,
272 BitmapSize,
273 TAG_MM);
274 ASSERT(Session->SystemSpaceBitMap);
275 RtlInitializeBitMap(Session->SystemSpaceBitMap,
276 (PULONG)(Session->SystemSpaceBitMap + 1),
277 (ULONG)(Size / MI_SYSTEM_VIEW_BUCKET_SIZE));
278
279 /* Set system space fully empty to begin with */
280 RtlClearAllBits(Session->SystemSpaceBitMap);
281
282 /* Set default hash flags */
283 Session->SystemSpaceHashSize = 31;
284 Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
285 Session->SystemSpaceHashEntries = 0;
286
287 /* Calculate how much space for the hash views we'll need */
288 AllocSize = sizeof(MMVIEW) * Session->SystemSpaceHashSize;
289 ASSERT(AllocSize < PAGE_SIZE);
290
291 /* Allocate and zero the view table */
292 Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session == &MmSession ?
293 NonPagedPool :
294 PagedPool,
295 AllocSize,
296 TAG_MM);
297 ASSERT(Session->SystemSpaceViewTable != NULL);
298 RtlZeroMemory(Session->SystemSpaceViewTable, AllocSize);
299
300 /* Success */
301 return TRUE;
302 }
303
304 PVOID
305 NTAPI
306 MiInsertInSystemSpace(IN PMMSESSION Session,
307 IN ULONG Buckets,
308 IN PCONTROL_AREA ControlArea)
309 {
310 PVOID Base;
311 ULONG Entry, Hash, i, HashSize;
312 PMMVIEW OldTable;
313 PAGED_CODE();
314
315 /* Stay within 4GB */
316 ASSERT(Buckets < MI_SYSTEM_VIEW_BUCKET_SIZE);
317
318 /* Lock system space */
319 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
320
321 /* Check if we're going to exhaust hash entries */
322 if ((Session->SystemSpaceHashEntries + 8) > Session->SystemSpaceHashSize)
323 {
324 /* Double the hash size */
325 HashSize = Session->SystemSpaceHashSize * 2;
326
327 /* Save the old table and allocate a new one */
328 OldTable = Session->SystemSpaceViewTable;
329 Session->SystemSpaceViewTable = ExAllocatePoolWithTag(Session ==
330 &MmSession ?
331 NonPagedPool :
332 PagedPool,
333 HashSize *
334 sizeof(MMVIEW),
335 TAG_MM);
336 if (!Session->SystemSpaceViewTable)
337 {
338 /* Failed to allocate a new table, keep the old one for now */
339 Session->SystemSpaceViewTable = OldTable;
340 }
341 else
342 {
343 /* Clear the new table and set the new ahsh and key */
344 RtlZeroMemory(Session->SystemSpaceViewTable, HashSize * sizeof(MMVIEW));
345 Session->SystemSpaceHashSize = HashSize;
346 Session->SystemSpaceHashKey = Session->SystemSpaceHashSize - 1;
347
348 /* Loop the old table */
349 for (i = 0; i < Session->SystemSpaceHashSize / 2; i++)
350 {
351 /* Check if the entry was valid */
352 if (OldTable[i].Entry)
353 {
354 /* Re-hash the old entry and search for space in the new table */
355 Hash = (OldTable[i].Entry >> 16) % Session->SystemSpaceHashKey;
356 while (Session->SystemSpaceViewTable[Hash].Entry)
357 {
358 /* Loop back at the beginning if we had an overflow */
359 if (++Hash >= Session->SystemSpaceHashSize) Hash = 0;
360 }
361
362 /* Write the old entry in the new table */
363 Session->SystemSpaceViewTable[Hash] = OldTable[i];
364 }
365 }
366
367 /* Free the old table */
368 ExFreePool(OldTable);
369 }
370 }
371
372 /* Check if we ran out */
373 if (Session->SystemSpaceHashEntries == Session->SystemSpaceHashSize)
374 {
375 DPRINT1("Ran out of system view hash entries\n");
376 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
377 return NULL;
378 }
379
380 /* Find space where to map this view */
381 i = RtlFindClearBitsAndSet(Session->SystemSpaceBitMap, Buckets, 0);
382 if (i == 0xFFFFFFFF)
383 {
384 /* Out of space, fail */
385 Session->BitmapFailures++;
386 DPRINT1("Out of system view space\n");
387 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
388 return NULL;
389 }
390
391 /* Compute the base address */
392 Base = (PVOID)((ULONG_PTR)Session->SystemSpaceViewStart + (i * MI_SYSTEM_VIEW_BUCKET_SIZE));
393
394 /* Get the hash entry for this allocation */
395 Entry = ((ULONG_PTR)Base & ~(MI_SYSTEM_VIEW_BUCKET_SIZE - 1)) + Buckets;
396 Hash = (Entry >> 16) % Session->SystemSpaceHashKey;
397
398 /* Loop hash entries until a free one is found */
399 while (Session->SystemSpaceViewTable[Hash].Entry)
400 {
401 /* Unless we overflow, in which case loop back at hash o */
402 if (++Hash >= Session->SystemSpaceHashSize) Hash = 0;
403 }
404
405 /* Add this entry into the hash table */
406 Session->SystemSpaceViewTable[Hash].Entry = Entry;
407 Session->SystemSpaceViewTable[Hash].ControlArea = ControlArea;
408
409 /* Hash entry found, increment total and return the base address */
410 Session->SystemSpaceHashEntries++;
411 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
412 return Base;
413 }
414
415 NTSTATUS
416 NTAPI
417 MiAddMappedPtes(IN PMMPTE FirstPte,
418 IN PFN_NUMBER PteCount,
419 IN PCONTROL_AREA ControlArea)
420 {
421 MMPTE TempPte;
422 PMMPTE PointerPte, ProtoPte, LastProtoPte, LastPte;
423 PSUBSECTION Subsection;
424
425 /* ARM3 doesn't support this yet */
426 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
427 ASSERT(ControlArea->u.Flags.Rom == 0);
428 ASSERT(ControlArea->FilePointer == NULL);
429
430 /* Sanity checks */
431 ASSERT(PteCount != 0);
432 ASSERT(ControlArea->NumberOfMappedViews >= 1);
433 ASSERT(ControlArea->NumberOfUserReferences >= 1);
434 ASSERT(ControlArea->NumberOfSectionReferences != 0);
435 ASSERT(ControlArea->u.Flags.BeingCreated == 0);
436 ASSERT(ControlArea->u.Flags.BeingDeleted == 0);
437 ASSERT(ControlArea->u.Flags.BeingPurged == 0);
438
439 /* Get the PTEs for the actual mapping */
440 PointerPte = FirstPte;
441 LastPte = FirstPte + PteCount;
442
443 /* Get the prototype PTEs that desribe the section mapping in the subsection */
444 Subsection = (PSUBSECTION)(ControlArea + 1);
445 ProtoPte = Subsection->SubsectionBase;
446 LastProtoPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
447
448 /* Loop the PTEs for the mapping */
449 while (PointerPte < LastPte)
450 {
451 /* We may have run out of prototype PTEs in this subsection */
452 if (ProtoPte >= LastProtoPte)
453 {
454 /* But we don't handle this yet */
455 ASSERT(FALSE);
456 }
457
458 /* The PTE should be completely clear */
459 ASSERT(PointerPte->u.Long == 0);
460
461 /* Build the prototype PTE and write it */
462 MI_MAKE_PROTOTYPE_PTE(&TempPte, ProtoPte);
463 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
464
465 /* Keep going */
466 PointerPte++;
467 ProtoPte++;
468 }
469
470 /* No failure path */
471 return STATUS_SUCCESS;
472 }
473
474 VOID
475 NTAPI
476 MiFillSystemPageDirectory(IN PVOID Base,
477 IN SIZE_T NumberOfBytes)
478 {
479 PMMPDE PointerPde, LastPde, SystemMapPde;
480 MMPDE TempPde;
481 PFN_NUMBER PageFrameIndex, ParentPage;
482 KIRQL OldIrql;
483 PAGED_CODE();
484
485 /* Find the PDEs needed for this mapping */
486 PointerPde = MiAddressToPde(Base);
487 LastPde = MiAddressToPde((PVOID)((ULONG_PTR)Base + NumberOfBytes - 1));
488
489 #if (_MI_PAGING_LEVELS == 2)
490 /* Find the system double-mapped PDE that describes this mapping */
491 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
492 #else
493 /* We don't have a double mapping */
494 SystemMapPde = PointerPde;
495 #endif
496
497 /* Use the PDE template and loop the PDEs */
498 TempPde = ValidKernelPde;
499 while (PointerPde <= LastPde)
500 {
501 /* Lock the PFN database */
502 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
503
504 /* Check if we don't already have this PDE mapped */
505 if (SystemMapPde->u.Hard.Valid == 0)
506 {
507 /* Grab a page for it */
508 MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
509 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
510 PageFrameIndex = MiRemoveZeroPage(MI_GET_NEXT_COLOR());
511 ASSERT(PageFrameIndex);
512 TempPde.u.Hard.PageFrameNumber = PageFrameIndex;
513
514 #if (_MI_PAGING_LEVELS == 2)
515 ParentPage = MmSystemPageDirectory[(PointerPde - MiAddressToPde(NULL)) / PDE_COUNT];
516 #else
517 ParentPage = MiPdeToPpe(PointerPde)->u.Hard.PageFrameNumber;
518 #endif
519 /* Initialize its PFN entry, with the parent system page directory page table */
520 MiInitializePfnForOtherProcess(PageFrameIndex,
521 (PMMPTE)PointerPde,
522 ParentPage);
523
524 /* Make the system PDE entry valid */
525 MI_WRITE_VALID_PDE(SystemMapPde, TempPde);
526
527 /* The system PDE entry might be the PDE itself, so check for this */
528 if (PointerPde->u.Hard.Valid == 0)
529 {
530 /* It's different, so make the real PDE valid too */
531 MI_WRITE_VALID_PDE(PointerPde, TempPde);
532 }
533 }
534
535 /* Release the lock and keep going with the next PDE */
536 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
537 SystemMapPde++;
538 PointerPde++;
539 }
540 }
541
542 NTSTATUS
543 NTAPI
544 MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea,
545 IN BOOLEAN FailIfSystemViews)
546 {
547 KIRQL OldIrql;
548
549 /* Flag not yet supported */
550 ASSERT(FailIfSystemViews == FALSE);
551
552 /* Lock the PFN database */
553 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
554
555 /* State not yet supported */
556 ASSERT(ControlArea->u.Flags.BeingPurged == 0);
557
558 /* Increase the reference counts */
559 ControlArea->NumberOfMappedViews++;
560 ControlArea->NumberOfUserReferences++;
561 ASSERT(ControlArea->NumberOfSectionReferences != 0);
562
563 /* Release the PFN lock and return success */
564 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
565 return STATUS_SUCCESS;
566 }
567
568 PSUBSECTION
569 NTAPI
570 MiLocateSubsection(IN PMMVAD Vad,
571 IN ULONG_PTR Vpn)
572 {
573 PSUBSECTION Subsection;
574 PCONTROL_AREA ControlArea;
575 ULONG_PTR PteOffset;
576
577 /* Get the control area */
578 ControlArea = Vad->ControlArea;
579 ASSERT(ControlArea->u.Flags.Rom == 0);
580 ASSERT(ControlArea->u.Flags.Image == 0);
581 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
582
583 /* Get the subsection */
584 Subsection = (PSUBSECTION)(ControlArea + 1);
585
586 /* We only support single-subsection segments */
587 ASSERT(Subsection->SubsectionBase != NULL);
588 ASSERT(Vad->FirstPrototypePte >= Subsection->SubsectionBase);
589 ASSERT(Vad->FirstPrototypePte < &Subsection->SubsectionBase[Subsection->PtesInSubsection]);
590
591 /* Compute the PTE offset */
592 PteOffset = Vpn - Vad->StartingVpn;
593 PteOffset += Vad->FirstPrototypePte - Subsection->SubsectionBase;
594
595 /* Again, we only support single-subsection segments */
596 ASSERT(PteOffset < 0xF0000000);
597 ASSERT(PteOffset < Subsection->PtesInSubsection);
598
599 /* Return the subsection */
600 return Subsection;
601 }
602
603 VOID
604 NTAPI
605 MiSegmentDelete(IN PSEGMENT Segment)
606 {
607 PCONTROL_AREA ControlArea;
608 SEGMENT_FLAGS SegmentFlags;
609 PSUBSECTION Subsection;
610 PMMPTE PointerPte, LastPte, PteForProto;
611 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);
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
1218 /* Get the segment for this section */
1219 Segment = ControlArea->Segment;
1220
1221 /* One can only reserve a file-based mapping, not shared memory! */
1222 if ((AllocationType & MEM_RESERVE) && !(ControlArea->FilePointer))
1223 {
1224 return STATUS_INVALID_PARAMETER_9;
1225 }
1226
1227 /* This flag determines alignment, but ARM3 does not yet support it */
1228 ASSERT((AllocationType & MEM_DOS_LIM) == 0);
1229
1230 /* First, increase the map count. No purging is supported yet */
1231 Status = MiCheckPurgeAndUpMapCount(ControlArea, FALSE);
1232 if (!NT_SUCCESS(Status)) return Status;
1233
1234 /* Check if the caller specified the view size */
1235 if (!(*ViewSize))
1236 {
1237 /* The caller did not, so pick a 64K aligned view size based on the offset */
1238 SectionOffset->LowPart &= ~(_64K - 1);
1239 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
1240 }
1241 else
1242 {
1243 /* A size was specified, align it to a 64K boundary */
1244 *ViewSize += SectionOffset->LowPart & (_64K - 1);
1245
1246 /* Align the offset as well to make this an aligned map */
1247 SectionOffset->LowPart &= ~((ULONG)_64K - 1);
1248 }
1249
1250 /* We must be dealing with a 64KB aligned offset. This is a Windows ASSERT */
1251 ASSERT((SectionOffset->LowPart & ((ULONG)_64K - 1)) == 0);
1252
1253 /* It's illegal to try to map more than 2GB */
1254 /* FIXME: Should dereference the control area */
1255 if (*ViewSize >= 0x80000000) return STATUS_INVALID_VIEW_SIZE;
1256
1257 /* Windows ASSERTs for this flag */
1258 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
1259
1260 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
1261 ASSERT(ControlArea->u.Flags.Rom == 0);
1262 Subsection = (PSUBSECTION)(ControlArea + 1);
1263
1264 /* Sections with extended segments are not supported in ARM3 */
1265 ASSERT(Segment->SegmentFlags.TotalNumberOfPtes4132 == 0);
1266
1267 /* Within this section, figure out which PTEs will describe the view */
1268 PteOffset = (PFN_NUMBER)(SectionOffset->QuadPart >> PAGE_SHIFT);
1269
1270 /* The offset must be in this segment's PTE chunk and it must be valid. Windows ASSERTs */
1271 ASSERT(PteOffset < Segment->TotalNumberOfPtes);
1272 ASSERT(((SectionOffset->QuadPart + *ViewSize + PAGE_SIZE - 1) >> PAGE_SHIFT) >= PteOffset);
1273
1274 /* In ARM3, only one subsection is used for now. It must contain these PTEs */
1275 ASSERT(PteOffset < Subsection->PtesInSubsection);
1276
1277 /* In ARM3, only page-file backed sections (shared memory) are supported now */
1278 ASSERT(ControlArea->FilePointer == NULL);
1279
1280 /* Windows ASSERTs for this too -- there must be a subsection base address */
1281 ASSERT(Subsection->SubsectionBase != NULL);
1282
1283 /* Compute how much commit space the segment will take */
1284 if ((CommitSize) && (Segment->NumberOfCommittedPages < Segment->TotalNumberOfPtes))
1285 {
1286 PointerPte = &Subsection->SubsectionBase[PteOffset];
1287 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
1288 QuotaCharge = (ULONG)(LastPte - PointerPte);
1289 }
1290
1291 /* ARM3 does not currently support large pages */
1292 ASSERT(Segment->SegmentFlags.LargePages == 0);
1293
1294 /* Did the caller specify an address? */
1295 if (!(*BaseAddress) && !(Section->Address.StartingVpn))
1296 {
1297 /* ARM3 does not support these flags yet */
1298 ASSERT(Process->VmTopDown == 0);
1299 ASSERT(ZeroBits == 0);
1300
1301 /* Which way should we search? */
1302 if (AllocationType & MEM_TOP_DOWN)
1303 {
1304 /* No, find an address top-down */
1305 Status = MiFindEmptyAddressRangeDownTree(*ViewSize,
1306 (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS,
1307 _64K,
1308 &Process->VadRoot,
1309 &StartAddress,
1310 (PMMADDRESS_NODE*)&Process->VadFreeHint);
1311 ASSERT(NT_SUCCESS(Status));
1312 }
1313 else
1314 {
1315 /* No, find an address bottom-up */
1316 Status = MiFindEmptyAddressRangeInTree(*ViewSize,
1317 _64K,
1318 &Process->VadRoot,
1319 (PMMADDRESS_NODE*)&Process->VadFreeHint,
1320 &StartAddress);
1321 ASSERT(NT_SUCCESS(Status));
1322 }
1323
1324 /* Get the ending address, which is the last piece we need for the VAD */
1325 EndingAddress = (StartAddress + *ViewSize - 1) | (PAGE_SIZE - 1);
1326 }
1327 else
1328 {
1329 /* Is it SEC_BASED, or did the caller manually specify an address? */
1330 if (!(*BaseAddress))
1331 {
1332 /* It is a SEC_BASED mapping, use the address that was generated */
1333 StartAddress = Section->Address.StartingVpn + SectionOffset->LowPart;
1334 DPRINT("BASED: 0x%p\n", StartAddress);
1335 }
1336 else
1337 {
1338 /* Just align what the caller gave us */
1339 StartAddress = ROUND_UP((ULONG_PTR)*BaseAddress, _64K);
1340 }
1341
1342 /* Get the ending address, which is the last piece we need for the VAD */
1343 EndingAddress = (StartAddress + *ViewSize - 1) | (PAGE_SIZE - 1);
1344
1345 /* Make sure it doesn't conflict with an existing allocation */
1346 if (MiCheckForConflictingNode(StartAddress >> PAGE_SHIFT,
1347 EndingAddress >> PAGE_SHIFT,
1348 &Process->VadRoot))
1349 {
1350 DPRINT1("Conflict with SEC_BASED or manually based section!\n");
1351 MiDereferenceControlArea(ControlArea);
1352 return STATUS_CONFLICTING_ADDRESSES;
1353 }
1354 }
1355
1356 /* A VAD can now be allocated. Do so and zero it out */
1357 /* FIXME: we are allocating a LONG VAD for ReactOS compatibility only */
1358 ASSERT((AllocationType & MEM_RESERVE) == 0); /* ARM3 does not support this */
1359 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'ldaV');
1360 if (!Vad)
1361 {
1362 MiDereferenceControlArea(ControlArea);
1363 return STATUS_INSUFFICIENT_RESOURCES;
1364 }
1365 RtlZeroMemory(Vad, sizeof(MMVAD_LONG));
1366 Vad->u4.Banked = (PVOID)0xDEADBABE;
1367
1368 /* Write all the data required in the VAD for handling a fault */
1369 Vad->StartingVpn = StartAddress >> PAGE_SHIFT;
1370 Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
1371 Vad->ControlArea = ControlArea;
1372 Vad->u.VadFlags.Protection = ProtectionMask;
1373 Vad->u2.VadFlags2.FileOffset = (ULONG)(SectionOffset->QuadPart >> 16);
1374 Vad->u2.VadFlags2.Inherit = (InheritDisposition == ViewShare);
1375 if ((AllocationType & SEC_NO_CHANGE) || (Section->u.Flags.NoChange))
1376 {
1377 /* This isn't really implemented yet, but handle setting the flag */
1378 Vad->u.VadFlags.NoChange = 1;
1379 Vad->u2.VadFlags2.SecNoChange = 1;
1380 }
1381
1382 /* Finally, write down the first and last prototype PTE */
1383 Vad->FirstPrototypePte = &Subsection->SubsectionBase[PteOffset];
1384 PteOffset += (Vad->EndingVpn - Vad->StartingVpn);
1385 ASSERT(PteOffset < Subsection->PtesInSubsection);
1386 Vad->LastContiguousPte = &Subsection->SubsectionBase[PteOffset];
1387
1388 /* Make sure the prototype PTE ranges make sense, this is a Windows ASSERT */
1389 ASSERT(Vad->FirstPrototypePte <= Vad->LastContiguousPte);
1390
1391 /* FIXME: Should setup VAD bitmap */
1392 Status = STATUS_SUCCESS;
1393
1394 /* Pretend as if we own the working set */
1395 MiLockProcessWorkingSetUnsafe(Process, Thread);
1396
1397 /* Insert the VAD */
1398 MiInsertVad((PMMVAD)Vad, Process);
1399
1400 /* Release the working set */
1401 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
1402
1403 /* Windows stores this for accounting purposes, do so as well */
1404 if (!Segment->u2.FirstMappedVa) Segment->u2.FirstMappedVa = (PVOID)StartAddress;
1405
1406 /* Check if anything was committed */
1407 if (QuotaCharge)
1408 {
1409 /* Set the start and end PTE addresses, and pick the template PTE */
1410 PointerPte = Vad->FirstPrototypePte;
1411 LastPte = PointerPte + BYTES_TO_PAGES(CommitSize);
1412 TempPte = Segment->SegmentPteTemplate;
1413
1414 /* Acquire the commit lock and loop all prototype PTEs to be committed */
1415 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
1416 while (PointerPte < LastPte)
1417 {
1418 /* Make sure the PTE is already invalid */
1419 if (PointerPte->u.Long == 0)
1420 {
1421 /* And write the invalid PTE */
1422 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
1423 }
1424 else
1425 {
1426 /* The PTE is valid, so skip it */
1427 QuotaExcess++;
1428 }
1429
1430 /* Move to the next PTE */
1431 PointerPte++;
1432 }
1433
1434 /* Now check how many pages exactly we committed, and update accounting */
1435 ASSERT(QuotaCharge >= QuotaExcess);
1436 QuotaCharge -= QuotaExcess;
1437 Segment->NumberOfCommittedPages += QuotaCharge;
1438 ASSERT(Segment->NumberOfCommittedPages <= Segment->TotalNumberOfPtes);
1439
1440 /* Now that we're done, release the lock */
1441 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
1442 }
1443
1444 /* Finally, let the caller know where, and for what size, the view was mapped */
1445 *ViewSize = (ULONG_PTR)EndingAddress - (ULONG_PTR)StartAddress + 1;
1446 *BaseAddress = (PVOID)StartAddress;
1447 DPRINT("Start and region: 0x%p, 0x%p\n", *BaseAddress, *ViewSize);
1448 return STATUS_SUCCESS;
1449 }
1450
1451 VOID
1452 NTAPI
1453 MiSubsectionConsistent(IN PSUBSECTION Subsection)
1454 {
1455 /* ReactOS only supports systems with 4K pages and 4K sectors */
1456 ASSERT(Subsection->u.SubsectionFlags.SectorEndOffset == 0);
1457
1458 /* Therefore, then number of PTEs should be equal to the number of sectors */
1459 if (Subsection->NumberOfFullSectors != Subsection->PtesInSubsection)
1460 {
1461 /* Break and warn if this is inconsistent */
1462 DPRINT1("Mm: Subsection inconsistent (%x vs %x)\n",
1463 Subsection->NumberOfFullSectors, Subsection->PtesInSubsection);
1464 DbgBreakPoint();
1465 }
1466 }
1467
1468 NTSTATUS
1469 NTAPI
1470 MiCreateDataFileMap(IN PFILE_OBJECT File,
1471 OUT PSEGMENT *Segment,
1472 IN PSIZE_T MaximumSize,
1473 IN ULONG SectionPageProtection,
1474 IN ULONG AllocationAttributes,
1475 IN ULONG IgnoreFileSizing)
1476 {
1477 /* Not yet implemented */
1478 ASSERT(FALSE);
1479 *Segment = NULL;
1480 return STATUS_NOT_IMPLEMENTED;
1481 }
1482
1483 NTSTATUS
1484 NTAPI
1485 MiCreatePagingFileMap(OUT PSEGMENT *Segment,
1486 IN PSIZE_T MaximumSize,
1487 IN ULONG ProtectionMask,
1488 IN ULONG AllocationAttributes)
1489 {
1490 SIZE_T SizeLimit;
1491 PFN_COUNT PteCount;
1492 PMMPTE PointerPte;
1493 MMPTE TempPte;
1494 PCONTROL_AREA ControlArea;
1495 PSEGMENT NewSegment;
1496 PSUBSECTION Subsection;
1497 PAGED_CODE();
1498
1499 /* No large pages in ARM3 yet */
1500 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
1501
1502 /* Pagefile-backed sections need a known size */
1503 if (!(*MaximumSize)) return STATUS_INVALID_PARAMETER_4;
1504
1505 /* Calculate the maximum size possible, given the Prototype PTEs we'll need */
1506 SizeLimit = MAXULONG_PTR - sizeof(SEGMENT);
1507 SizeLimit /= sizeof(MMPTE);
1508 SizeLimit <<= PAGE_SHIFT;
1509
1510 /* Fail if this size is too big */
1511 if (*MaximumSize > SizeLimit) return STATUS_SECTION_TOO_BIG;
1512
1513 /* Calculate how many Prototype PTEs will be needed */
1514 PteCount = (PFN_COUNT)((*MaximumSize + PAGE_SIZE - 1) >> PAGE_SHIFT);
1515
1516 /* For commited memory, we must have a valid protection mask */
1517 if (AllocationAttributes & SEC_COMMIT) ASSERT(ProtectionMask != 0);
1518
1519 /* The segment contains all the Prototype PTEs, allocate it in paged pool */
1520 NewSegment = ExAllocatePoolWithTag(PagedPool,
1521 sizeof(SEGMENT) +
1522 sizeof(MMPTE) * (PteCount - 1),
1523 'tSmM');
1524 ASSERT(NewSegment);
1525 *Segment = NewSegment;
1526
1527 /* Now allocate the control area, which has the subsection structure */
1528 ControlArea = ExAllocatePoolWithTag(NonPagedPool,
1529 sizeof(CONTROL_AREA) + sizeof(SUBSECTION),
1530 'tCmM');
1531 ASSERT(ControlArea);
1532
1533 /* And zero it out, filling the basic segmnet pointer and reference fields */
1534 RtlZeroMemory(ControlArea, sizeof(CONTROL_AREA) + sizeof(SUBSECTION));
1535 ControlArea->Segment = NewSegment;
1536 ControlArea->NumberOfSectionReferences = 1;
1537 ControlArea->NumberOfUserReferences = 1;
1538
1539 /* Convert allocation attributes to control area flags */
1540 if (AllocationAttributes & SEC_BASED) ControlArea->u.Flags.Based = 1;
1541 if (AllocationAttributes & SEC_RESERVE) ControlArea->u.Flags.Reserve = 1;
1542 if (AllocationAttributes & SEC_COMMIT) ControlArea->u.Flags.Commit = 1;
1543
1544 /* The subsection follows, write the mask, PTE count and point back to the CA */
1545 Subsection = (PSUBSECTION)(ControlArea + 1);
1546 Subsection->ControlArea = ControlArea;
1547 Subsection->PtesInSubsection = PteCount;
1548 Subsection->u.SubsectionFlags.Protection = ProtectionMask;
1549
1550 /* Zero out the segment's prototype PTEs, and link it with the control area */
1551 PointerPte = &NewSegment->ThePtes[0];
1552 RtlZeroMemory(NewSegment, sizeof(SEGMENT));
1553 NewSegment->PrototypePte = PointerPte;
1554 NewSegment->ControlArea = ControlArea;
1555
1556 /* Save some extra accounting data for the segment as well */
1557 NewSegment->u1.CreatingProcess = PsGetCurrentProcess();
1558 NewSegment->SizeOfSegment = PteCount * PAGE_SIZE;
1559 NewSegment->TotalNumberOfPtes = PteCount;
1560 NewSegment->NonExtendedPtes = PteCount;
1561
1562 /* The subsection's base address is the first Prototype PTE in the segment */
1563 Subsection->SubsectionBase = PointerPte;
1564
1565 /* Start with an empty PTE, unless this is a commit operation */
1566 TempPte.u.Long = 0;
1567 if (AllocationAttributes & SEC_COMMIT)
1568 {
1569 /* In which case, write down the protection mask in the Prototype PTEs */
1570 TempPte.u.Soft.Protection = ProtectionMask;
1571
1572 /* For accounting, also mark these pages as being committed */
1573 NewSegment->NumberOfCommittedPages = PteCount;
1574 }
1575
1576 /* The template PTE itself for the segment should also have the mask set */
1577 NewSegment->SegmentPteTemplate.u.Soft.Protection = ProtectionMask;
1578
1579 /* Write out the prototype PTEs, for now they're simply demand zero */
1580 #ifdef _WIN64
1581 RtlFillMemoryUlonglong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1582 #else
1583 RtlFillMemoryUlong(PointerPte, PteCount * sizeof(MMPTE), TempPte.u.Long);
1584 #endif
1585 return STATUS_SUCCESS;
1586 }
1587
1588 PFILE_OBJECT
1589 NTAPI
1590 MmGetFileObjectForSection(IN PVOID SectionObject)
1591 {
1592 PSECTION_OBJECT Section;
1593 ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
1594 ASSERT(SectionObject != NULL);
1595
1596 /* Check if it's an ARM3, or ReactOS section */
1597 if (MiIsRosSectionObject(SectionObject) == FALSE)
1598 {
1599 /* Return the file pointer stored in the control area */
1600 Section = SectionObject;
1601 return Section->Segment->ControlArea->FilePointer;
1602 }
1603
1604 /* Return the file object */
1605 return ((PROS_SECTION_OBJECT)SectionObject)->FileObject;
1606 }
1607
1608 VOID
1609 NTAPI
1610 MmGetImageInformation (OUT PSECTION_IMAGE_INFORMATION ImageInformation)
1611 {
1612 PSECTION_OBJECT SectionObject;
1613
1614 /* Get the section object of this process*/
1615 SectionObject = PsGetCurrentProcess()->SectionObject;
1616 ASSERT(SectionObject != NULL);
1617 ASSERT(MiIsRosSectionObject(SectionObject) == TRUE);
1618
1619 /* Return the image information */
1620 DPRINT1("HERE!\n");
1621 *ImageInformation = ((PROS_SECTION_OBJECT)SectionObject)->ImageSection->ImageInformation;
1622 }
1623
1624 NTSTATUS
1625 NTAPI
1626 MmGetFileNameForFileObject(IN PFILE_OBJECT FileObject,
1627 OUT POBJECT_NAME_INFORMATION *ModuleName)
1628 {
1629 POBJECT_NAME_INFORMATION ObjectNameInfo;
1630 NTSTATUS Status;
1631 ULONG ReturnLength;
1632
1633 /* Allocate memory for our structure */
1634 ObjectNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, TAG_MM);
1635 if (!ObjectNameInfo) return STATUS_NO_MEMORY;
1636
1637 /* Query the name */
1638 Status = ObQueryNameString(FileObject,
1639 ObjectNameInfo,
1640 1024,
1641 &ReturnLength);
1642 if (!NT_SUCCESS(Status))
1643 {
1644 /* Failed, free memory */
1645 DPRINT1("Name query failed\n");
1646 ExFreePoolWithTag(ObjectNameInfo, TAG_MM);
1647 *ModuleName = NULL;
1648 return Status;
1649 }
1650
1651 /* Success */
1652 *ModuleName = ObjectNameInfo;
1653 return STATUS_SUCCESS;
1654 }
1655
1656 NTSTATUS
1657 NTAPI
1658 MmGetFileNameForSection(IN PVOID Section,
1659 OUT POBJECT_NAME_INFORMATION *ModuleName)
1660 {
1661 PFILE_OBJECT FileObject;
1662
1663 /* Make sure it's an image section */
1664 if (MiIsRosSectionObject(Section) == FALSE)
1665 {
1666 /* Check ARM3 Section flag */
1667 if (((PSECTION)Section)->u.Flags.Image == 0)
1668 {
1669 /* It's not, fail */
1670 DPRINT1("Not an image section\n");
1671 return STATUS_SECTION_NOT_IMAGE;
1672 }
1673 }
1674 else if (!(((PROS_SECTION_OBJECT)Section)->AllocationAttributes & SEC_IMAGE))
1675 {
1676 /* It's not, fail */
1677 DPRINT1("Not an image section\n");
1678 return STATUS_SECTION_NOT_IMAGE;
1679 }
1680
1681 /* Get the file object */
1682 FileObject = MmGetFileObjectForSection(Section);
1683 return MmGetFileNameForFileObject(FileObject, ModuleName);
1684 }
1685
1686 NTSTATUS
1687 NTAPI
1688 MmGetFileNameForAddress(IN PVOID Address,
1689 OUT PUNICODE_STRING ModuleName)
1690 {
1691 PVOID Section;
1692 PMEMORY_AREA MemoryArea;
1693 POBJECT_NAME_INFORMATION ModuleNameInformation;
1694 PVOID AddressSpace;
1695 NTSTATUS Status;
1696 PFILE_OBJECT FileObject = NULL;
1697 PMMVAD Vad;
1698 PCONTROL_AREA ControlArea;
1699
1700 /* Lock address space */
1701 AddressSpace = MmGetCurrentAddressSpace();
1702 MmLockAddressSpace(AddressSpace);
1703
1704 /* Locate the memory area for the process by address */
1705 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
1706 if (!MemoryArea)
1707 {
1708 /* Fail, the address does not exist */
1709 InvalidAddress:
1710 DPRINT1("Invalid address\n");
1711 MmUnlockAddressSpace(AddressSpace);
1712 return STATUS_INVALID_ADDRESS;
1713 }
1714
1715 /* Check if it's a section view (RosMm section) or ARM3 section */
1716 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
1717 {
1718 /* Get the section pointer to the SECTION_OBJECT */
1719 Section = MemoryArea->Data.SectionData.Section;
1720
1721 /* Unlock address space */
1722 MmUnlockAddressSpace(AddressSpace);
1723
1724 /* Get the filename of the section */
1725 Status = MmGetFileNameForSection(Section, &ModuleNameInformation);
1726 }
1727 else if (MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3)
1728 {
1729 /* Get the VAD */
1730 Vad = MiLocateAddress(Address);
1731 if (!Vad) goto InvalidAddress;
1732
1733 /* Make sure it's not a VM VAD */
1734 if (Vad->u.VadFlags.PrivateMemory == 1)
1735 {
1736 NotSection:
1737 DPRINT1("Address is not a section\n");
1738 MmUnlockAddressSpace(AddressSpace);
1739 return STATUS_SECTION_NOT_IMAGE;
1740 }
1741
1742 /* Get the control area */
1743 ControlArea = Vad->ControlArea;
1744 if (!(ControlArea) || !(ControlArea->u.Flags.Image)) goto NotSection;
1745
1746 /* Get the file object */
1747 FileObject = ControlArea->FilePointer;
1748 ASSERT(FileObject != NULL);
1749 ObReferenceObject(FileObject);
1750
1751 /* Unlock address space */
1752 MmUnlockAddressSpace(AddressSpace);
1753
1754 /* Get the filename of the file object */
1755 Status = MmGetFileNameForFileObject(FileObject, &ModuleNameInformation);
1756
1757 /* Dereference it */
1758 ObDereferenceObject(FileObject);
1759 }
1760 else
1761 {
1762 /* Trying to access virtual memory or something */
1763 goto InvalidAddress;
1764 }
1765
1766 /* Check if we were able to get the file object name */
1767 if (NT_SUCCESS(Status))
1768 {
1769 /* Init modulename */
1770 RtlCreateUnicodeString(ModuleName,
1771 ModuleNameInformation->Name.Buffer);
1772
1773 /* Free temp taged buffer from MmGetFileNameForFileObject() */
1774 ExFreePoolWithTag(ModuleNameInformation, TAG_MM);
1775 DPRINT("Found ModuleName %S by address %p\n", ModuleName->Buffer, Address);
1776 }
1777
1778 /* Return status */
1779 return Status;
1780 }
1781
1782 NTSTATUS
1783 NTAPI
1784 MiQueryMemorySectionName(IN HANDLE ProcessHandle,
1785 IN PVOID BaseAddress,
1786 OUT PVOID MemoryInformation,
1787 IN SIZE_T MemoryInformationLength,
1788 OUT PSIZE_T ReturnLength)
1789 {
1790 PEPROCESS Process;
1791 NTSTATUS Status;
1792 WCHAR ModuleFileNameBuffer[MAX_PATH] = {0};
1793 UNICODE_STRING ModuleFileName;
1794 PMEMORY_SECTION_NAME SectionName = NULL;
1795 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1796
1797 Status = ObReferenceObjectByHandle(ProcessHandle,
1798 PROCESS_QUERY_INFORMATION,
1799 NULL,
1800 PreviousMode,
1801 (PVOID*)(&Process),
1802 NULL);
1803
1804 if (!NT_SUCCESS(Status))
1805 {
1806 DPRINT("MiQueryMemorySectionName: ObReferenceObjectByHandle returned %x\n",Status);
1807 return Status;
1808 }
1809
1810 RtlInitEmptyUnicodeString(&ModuleFileName, ModuleFileNameBuffer, sizeof(ModuleFileNameBuffer));
1811 Status = MmGetFileNameForAddress(BaseAddress, &ModuleFileName);
1812
1813 if (NT_SUCCESS(Status))
1814 {
1815 SectionName = MemoryInformation;
1816 if (PreviousMode != KernelMode)
1817 {
1818 _SEH2_TRY
1819 {
1820 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1821 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1822 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1823
1824 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1825
1826 }
1827 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1828 {
1829 Status = _SEH2_GetExceptionCode();
1830 }
1831 _SEH2_END;
1832 }
1833 else
1834 {
1835 RtlInitUnicodeString(&SectionName->SectionFileName, SectionName->NameBuffer);
1836 SectionName->SectionFileName.MaximumLength = (USHORT)MemoryInformationLength;
1837 RtlCopyUnicodeString(&SectionName->SectionFileName, &ModuleFileName);
1838
1839 if (ReturnLength) *ReturnLength = ModuleFileName.Length;
1840
1841 }
1842 }
1843 ObDereferenceObject(Process);
1844 return Status;
1845 }
1846
1847 VOID
1848 NTAPI
1849 MiFlushTbAndCapture(IN PMMVAD FoundVad,
1850 IN PMMPTE PointerPte,
1851 IN ULONG ProtectionMask,
1852 IN PMMPFN Pfn1,
1853 IN BOOLEAN CaptureDirtyBit)
1854 {
1855 MMPTE TempPte, PreviousPte;
1856 KIRQL OldIrql;
1857 BOOLEAN RebuildPte = FALSE;
1858
1859 //
1860 // User for sanity checking later on
1861 //
1862 PreviousPte = *PointerPte;
1863
1864 //
1865 // Build the PTE and acquire the PFN lock
1866 //
1867 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1868 PointerPte,
1869 ProtectionMask,
1870 PreviousPte.u.Hard.PageFrameNumber);
1871 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1872
1873 //
1874 // We don't support I/O mappings in this path yet
1875 //
1876 ASSERT(Pfn1 != NULL);
1877 ASSERT(Pfn1->u3.e1.CacheAttribute != MiWriteCombined);
1878
1879 //
1880 // Make sure new protection mask doesn't get in conflict and fix it if it does
1881 //
1882 if (Pfn1->u3.e1.CacheAttribute == MiCached)
1883 {
1884 //
1885 // This is a cached PFN
1886 //
1887 if (ProtectionMask & (MM_NOCACHE | MM_NOACCESS))
1888 {
1889 RebuildPte = TRUE;
1890 ProtectionMask &= ~(MM_NOCACHE | MM_NOACCESS);
1891 }
1892 }
1893 else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
1894 {
1895 //
1896 // This is a non-cached PFN
1897 //
1898 if ((ProtectionMask & (MM_NOCACHE | MM_NOACCESS)) != MM_NOCACHE)
1899 {
1900 RebuildPte = TRUE;
1901 ProtectionMask &= ~MM_NOACCESS;
1902 ProtectionMask |= MM_NOCACHE;
1903 }
1904 }
1905
1906 if (RebuildPte)
1907 {
1908 MI_MAKE_HARDWARE_PTE_USER(&TempPte,
1909 PointerPte,
1910 ProtectionMask,
1911 PreviousPte.u.Hard.PageFrameNumber);
1912 }
1913
1914 //
1915 // Write the new PTE, making sure we are only changing the bits
1916 //
1917 MI_UPDATE_VALID_PTE(PointerPte, TempPte);
1918
1919 //
1920 // Flush the TLB
1921 //
1922 ASSERT(PreviousPte.u.Hard.Valid == 1);
1923 KeFlushCurrentTb();
1924 ASSERT(PreviousPte.u.Hard.Valid == 1);
1925
1926 //
1927 // Windows updates the relevant PFN1 information, we currently don't.
1928 //
1929 if (CaptureDirtyBit) DPRINT1("Warning, not handling dirty bit\n");
1930
1931 //
1932 // Not supported in ARM3
1933 //
1934 ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch);
1935
1936 //
1937 // Release the PFN lock, we are done
1938 //
1939 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1940 }
1941
1942 //
1943 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
1944 //
1945 NTSTATUS
1946 NTAPI
1947 MiSetProtectionOnSection(IN PEPROCESS Process,
1948 IN PMMVAD FoundVad,
1949 IN PVOID StartingAddress,
1950 IN PVOID EndingAddress,
1951 IN ULONG NewProtect,
1952 OUT PULONG CapturedOldProtect,
1953 IN ULONG DontCharge,
1954 OUT PULONG Locked)
1955 {
1956 PMMPTE PointerPte, LastPte;
1957 MMPTE TempPte, PteContents;
1958 PMMPDE PointerPde;
1959 PMMPFN Pfn1;
1960 ULONG ProtectionMask, QuotaCharge = 0;
1961 PETHREAD Thread = PsGetCurrentThread();
1962 PAGED_CODE();
1963
1964 //
1965 // Tell caller nothing is being locked
1966 //
1967 *Locked = FALSE;
1968
1969 //
1970 // This function should only be used for section VADs. Windows ASSERT */
1971 //
1972 ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0);
1973
1974 //
1975 // We don't support these features in ARM3
1976 //
1977 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
1978 ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0);
1979
1980 //
1981 // Convert and validate the protection mask
1982 //
1983 ProtectionMask = MiMakeProtectionMask(NewProtect);
1984 if (ProtectionMask == MM_INVALID_PROTECTION)
1985 {
1986 DPRINT1("Invalid section protect\n");
1987 return STATUS_INVALID_PAGE_PROTECTION;
1988 }
1989
1990 //
1991 // Get the PTE and PDE for the address, as well as the final PTE
1992 //
1993 MiLockProcessWorkingSetUnsafe(Process, Thread);
1994 PointerPde = MiAddressToPde(StartingAddress);
1995 PointerPte = MiAddressToPte(StartingAddress);
1996 LastPte = MiAddressToPte(EndingAddress);
1997
1998 //
1999 // Make the PDE valid, and check the status of the first PTE
2000 //
2001 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2002 if (PointerPte->u.Long)
2003 {
2004 //
2005 // Not supported in ARM3
2006 //
2007 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
2008
2009 //
2010 // Capture the page protection and make the PDE valid
2011 //
2012 *CapturedOldProtect = MiGetPageProtection(PointerPte);
2013 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2014 }
2015 else
2016 {
2017 //
2018 // Only pagefile-backed section VADs are supported for now
2019 //
2020 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2021
2022 //
2023 // Grab the old protection from the VAD itself
2024 //
2025 *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection];
2026 }
2027
2028 //
2029 // Loop all the PTEs now
2030 //
2031 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2032 while (PointerPte <= LastPte)
2033 {
2034 //
2035 // Check if we've crossed a PDE boundary and make the new PDE valid too
2036 //
2037 if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
2038 {
2039 PointerPde = MiAddressToPte(PointerPte);
2040 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2041 }
2042
2043 //
2044 // Capture the PTE and see what we're dealing with
2045 //
2046 PteContents = *PointerPte;
2047 if (PteContents.u.Long == 0)
2048 {
2049 //
2050 // This used to be a zero PTE and it no longer is, so we must add a
2051 // reference to the pagetable.
2052 //
2053 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2054
2055 //
2056 // Create the demand-zero prototype PTE
2057 //
2058 TempPte = PrototypePte;
2059 TempPte.u.Soft.Protection = ProtectionMask;
2060 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2061 }
2062 else if (PteContents.u.Hard.Valid == 1)
2063 {
2064 //
2065 // Get the PFN entry
2066 //
2067 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2068
2069 //
2070 // We don't support these yet
2071 //
2072 ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0);
2073 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2074
2075 //
2076 // Write the protection mask and write it with a TLB flush
2077 //
2078 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2079 MiFlushTbAndCapture(FoundVad,
2080 PointerPte,
2081 ProtectionMask,
2082 Pfn1,
2083 TRUE);
2084 }
2085 else
2086 {
2087 //
2088 // We don't support these cases yet
2089 //
2090 ASSERT(PteContents.u.Soft.Prototype == 0);
2091 ASSERT(PteContents.u.Soft.Transition == 0);
2092
2093 //
2094 // The PTE is already demand-zero, just update the protection mask
2095 //
2096 PointerPte->u.Soft.Protection = ProtectionMask;
2097 }
2098
2099 PointerPte++;
2100 }
2101
2102 //
2103 // Unlock the working set and update quota charges if needed, then return
2104 //
2105 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2106 if ((QuotaCharge > 0) && (!DontCharge))
2107 {
2108 FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
2109 Process->CommitCharge -= QuotaCharge;
2110 }
2111 return STATUS_SUCCESS;
2112 }
2113
2114 VOID
2115 NTAPI
2116 MiRemoveMappedPtes(IN PVOID BaseAddress,
2117 IN ULONG NumberOfPtes,
2118 IN PCONTROL_AREA ControlArea,
2119 IN PMMSUPPORT Ws)
2120 {
2121 PMMPTE PointerPte;//, FirstPte;
2122 PMMPDE PointerPde, SystemMapPde;
2123 PMMPFN Pfn1, Pfn2;
2124 MMPTE PteContents;
2125 KIRQL OldIrql;
2126 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress);
2127
2128 /* Get the PTE and loop each one */
2129 PointerPte = MiAddressToPte(BaseAddress);
2130 //FirstPte = PointerPte;
2131 while (NumberOfPtes)
2132 {
2133 /* Check if the PTE is already valid */
2134 PteContents = *PointerPte;
2135 if (PteContents.u.Hard.Valid == 1)
2136 {
2137 /* Get the PFN entry */
2138 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2139
2140 /* Get the PTE */
2141 PointerPde = MiAddressToPte(PointerPte);
2142
2143 /* Lock the PFN database and make sure this isn't a mapped file */
2144 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2145 ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0);
2146
2147 /* FIXME: Dirty bit management */
2148
2149 /* Was the PDE invalid */
2150 if (PointerPde->u.Long == 0)
2151 {
2152 #if (_MI_PAGING_LEVELS == 2)
2153 /* Find the system double-mapped PDE that describes this mapping */
2154 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
2155
2156 /* Make it valid */
2157 ASSERT(SystemMapPde->u.Hard.Valid == 1);
2158 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde);
2159 #else
2160 ASSERT(FALSE);
2161 #endif
2162 }
2163
2164 /* Dereference the PDE and the PTE */
2165 Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
2166 //MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2167 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
2168 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2169
2170 /* Release the PFN lock */
2171 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2172 }
2173 else
2174 {
2175 /* Windows ASSERT */
2176 ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1));
2177
2178 /* But not handled in ARM3 */
2179 ASSERT(PteContents.u.Soft.Prototype == 0);
2180 }
2181
2182 /* Make the PTE into a zero PTE */
2183 PointerPte->u.Long = 0;
2184
2185 /* Move to the next PTE */
2186 PointerPte++;
2187 NumberOfPtes--;
2188 }
2189
2190 /* Flush the TLB */
2191 KeFlushCurrentTb();
2192
2193 /* Acquire the PFN lock */
2194 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2195
2196 /* Decrement the accounting counters */
2197 ControlArea->NumberOfUserReferences--;
2198 ControlArea->NumberOfMappedViews--;
2199
2200 /* Check if we should destroy the CA and release the lock */
2201 MiCheckControlArea(ControlArea, OldIrql);
2202 }
2203
2204 ULONG
2205 NTAPI
2206 MiRemoveFromSystemSpace(IN PMMSESSION Session,
2207 IN PVOID Base,
2208 OUT PCONTROL_AREA *ControlArea)
2209 {
2210 ULONG Hash, Size, Count = 0;
2211 ULONG_PTR Entry;
2212 PAGED_CODE();
2213
2214 /* Compute the hash for this entry and loop trying to find it */
2215 Entry = (ULONG_PTR)Base >> 16;
2216 Hash = Entry % Session->SystemSpaceHashKey;
2217 while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry)
2218 {
2219 /* Check if we overflew past the end of the hash table */
2220 if (++Hash >= Session->SystemSpaceHashSize)
2221 {
2222 /* Reset the hash to zero and keep searching from the bottom */
2223 Hash = 0;
2224 if (++Count == 2)
2225 {
2226 /* But if we overflew twice, then this is not a real mapping */
2227 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2228 (ULONG_PTR)Base,
2229 1,
2230 0,
2231 0);
2232 }
2233 }
2234 }
2235
2236 /* One less entry */
2237 Session->SystemSpaceHashEntries--;
2238
2239 /* Extract the size and clear the entry */
2240 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2241 Session->SystemSpaceViewTable[Hash].Entry = 0;
2242
2243 /* Return the control area and the size */
2244 *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2245 return Size;
2246 }
2247
2248 NTSTATUS
2249 NTAPI
2250 MiUnmapViewInSystemSpace(IN PMMSESSION Session,
2251 IN PVOID MappedBase)
2252 {
2253 ULONG Size;
2254 PCONTROL_AREA ControlArea;
2255 PAGED_CODE();
2256
2257 /* Remove this mapping */
2258 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2259 Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
2260
2261 /* Clear the bits for this mapping */
2262 RtlClearBits(Session->SystemSpaceBitMap,
2263 (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16),
2264 Size);
2265
2266 /* Convert the size from a bit size into the actual size */
2267 Size = Size * (_64K >> PAGE_SHIFT);
2268
2269 /* Remove the PTEs now */
2270 MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL);
2271 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2272
2273 /* Return success */
2274 return STATUS_SUCCESS;
2275 }
2276
2277 /* PUBLIC FUNCTIONS ***********************************************************/
2278
2279 /*
2280 * @implemented
2281 */
2282 NTSTATUS
2283 NTAPI
2284 MmCreateArm3Section(OUT PVOID *SectionObject,
2285 IN ACCESS_MASK DesiredAccess,
2286 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2287 IN PLARGE_INTEGER InputMaximumSize,
2288 IN ULONG SectionPageProtection,
2289 IN ULONG AllocationAttributes,
2290 IN HANDLE FileHandle OPTIONAL,
2291 IN PFILE_OBJECT FileObject OPTIONAL)
2292 {
2293 SECTION Section;
2294 PSECTION NewSection;
2295 PSUBSECTION Subsection;
2296 PSEGMENT NewSegment, Segment;
2297 NTSTATUS Status;
2298 PCONTROL_AREA ControlArea;
2299 ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge;
2300 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2301 BOOLEAN FileLock = FALSE, KernelCall = FALSE;
2302 KIRQL OldIrql;
2303 PFILE_OBJECT File;
2304 PVOID PreviousSectionPointer;
2305
2306 /* Make the same sanity checks that the Nt interface should've validated */
2307 ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
2308 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
2309 SEC_NO_CHANGE)) == 0);
2310 ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
2311 ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
2312 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
2313 SEC_NOCACHE | SEC_NO_CHANGE))));
2314 ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
2315 ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
2316 (SectionPageProtection & PAGE_WRITECOMBINE) ||
2317 (SectionPageProtection & PAGE_GUARD) ||
2318 (SectionPageProtection & PAGE_NOACCESS)));
2319
2320 /* Convert section flag to page flag */
2321 if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
2322
2323 /* Check to make sure the protection is correct. Nt* does this already */
2324 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
2325 if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
2326
2327 /* Check if this is going to be a data or image backed file section */
2328 if ((FileHandle) || (FileObject))
2329 {
2330 /* These cannot be mapped with large pages */
2331 if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6;
2332
2333 /* For now, only support the mechanism through a file handle */
2334 ASSERT(FileObject == NULL);
2335
2336 /* Reference the file handle to get the object */
2337 Status = ObReferenceObjectByHandle(FileHandle,
2338 MmMakeFileAccess[ProtectionMask],
2339 IoFileObjectType,
2340 PreviousMode,
2341 (PVOID*)&File,
2342 NULL);
2343 if (!NT_SUCCESS(Status)) return Status;
2344
2345 /* Make sure Cc has been doing its job */
2346 if (!File->SectionObjectPointer)
2347 {
2348 /* This is not a valid file system-based file, fail */
2349 ObDereferenceObject(File);
2350 return STATUS_INVALID_FILE_FOR_SECTION;
2351 }
2352
2353 /* Image-file backed sections are not yet supported */
2354 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2355
2356 /* Compute the size of the control area, and allocate it */
2357 ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
2358 ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM');
2359 if (!ControlArea)
2360 {
2361 ObDereferenceObject(File);
2362 return STATUS_INSUFFICIENT_RESOURCES;
2363 }
2364
2365 /* Zero it out */
2366 RtlZeroMemory(ControlArea, ControlAreaSize);
2367
2368 /* Did we get a handle, or an object? */
2369 if (FileHandle)
2370 {
2371 /* We got a file handle so we have to lock down the file */
2372 #if 0
2373 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection);
2374 if (!NT_SUCCESS(Status))
2375 {
2376 ExFreePool(ControlArea);
2377 ObDereferenceObject(File);
2378 return Status;
2379 }
2380 #else
2381 /* ReactOS doesn't support this API yet, so do nothing */
2382 Status = STATUS_SUCCESS;
2383 #endif
2384 /* Update the top-level IRP so that drivers know what's happening */
2385 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
2386 FileLock = TRUE;
2387 }
2388
2389 /* Lock the PFN database while we play with the section pointers */
2390 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2391
2392 /* Image-file backed sections are not yet supported */
2393 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2394
2395 /* There should not already be a control area for this file */
2396 ASSERT(File->SectionObjectPointer->DataSectionObject == NULL);
2397 NewSegment = NULL;
2398
2399 /* Write down that this CA is being created, and set it */
2400 ControlArea->u.Flags.BeingCreated = TRUE;
2401 PreviousSectionPointer = File->SectionObjectPointer;
2402 File->SectionObjectPointer->DataSectionObject = ControlArea;
2403
2404 /* We can release the PFN lock now */
2405 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2406
2407 /* We don't support previously-mapped file */
2408 ASSERT(NewSegment == NULL);
2409
2410 /* Image-file backed sections are not yet supported */
2411 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2412
2413 /* So we always create a data file map */
2414 Status = MiCreateDataFileMap(File,
2415 &Segment,
2416 (PSIZE_T)InputMaximumSize,
2417 SectionPageProtection,
2418 AllocationAttributes,
2419 KernelCall);
2420 ASSERT(PreviousSectionPointer == File->SectionObjectPointer);
2421 ASSERT(NT_SUCCESS(Status));
2422
2423 /* Check if a maximum size was specified */
2424 if (!InputMaximumSize->QuadPart)
2425 {
2426 /* Nope, use the segment size */
2427 Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
2428 }
2429 else
2430 {
2431 /* Yep, use the entered size */
2432 Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart;
2433 }
2434 }
2435 else
2436 {
2437 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2438 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
2439
2440 /* Not yet supported */
2441 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
2442
2443 /* So this must be a pagefile-backed section, create the mappings needed */
2444 Status = MiCreatePagingFileMap(&NewSegment,
2445 (PSIZE_T)InputMaximumSize,
2446 ProtectionMask,
2447 AllocationAttributes);
2448 if (!NT_SUCCESS(Status)) return Status;
2449
2450 /* Set the size here, and read the control area */
2451 Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
2452 ControlArea = NewSegment->ControlArea;
2453 }
2454
2455 /* Did we already have a segment? */
2456 if (!NewSegment)
2457 {
2458 /* This must be the file path and we created a segment */
2459 NewSegment = Segment;
2460 ASSERT(File != NULL);
2461
2462 /* Acquire the PFN lock while we set control area flags */
2463 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2464
2465 /* We don't support this race condition yet, so assume no waiters */
2466 ASSERT(ControlArea->WaitingForDeletion == NULL);
2467 ControlArea->WaitingForDeletion = NULL;
2468
2469 /* Image-file backed sections are not yet supported, nor ROM images */
2470 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2471 ASSERT(Segment->ControlArea->u.Flags.Rom == 0);
2472
2473 /* Take off the being created flag, and then release the lock */
2474 ControlArea->u.Flags.BeingCreated = FALSE;
2475 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2476 }
2477
2478 /* Check if we locked the file earlier */
2479 if (FileLock)
2480 {
2481 /* Reset the top-level IRP and release the lock */
2482 IoSetTopLevelIrp(NULL);
2483 //FsRtlReleaseFile(File);
2484 FileLock = FALSE;
2485 }
2486
2487 /* Set the initial section object data */
2488 Section.InitialPageProtection = SectionPageProtection;
2489
2490 /* The mapping created a control area and segment, save the flags */
2491 Section.Segment = NewSegment;
2492 Section.u.LongFlags = ControlArea->u.LongFlags;
2493
2494 /* Check if this is a user-mode read-write non-image file mapping */
2495 if (!(FileObject) &&
2496 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2497 (ControlArea->u.Flags.Image == 0) &&
2498 (ControlArea->FilePointer != NULL))
2499 {
2500 /* Add a reference and set the flag */
2501 Section.u.Flags.UserWritable = 1;
2502 InterlockedIncrement((PLONG)&ControlArea->WritableUserReferences);
2503 }
2504
2505 /* Check for image mappings or page file mappings */
2506 if ((ControlArea->u.Flags.Image == 1) || !(ControlArea->FilePointer))
2507 {
2508 /* Charge the segment size, and allocate a subsection */
2509 PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
2510 Size = sizeof(SUBSECTION);
2511 }
2512 else
2513 {
2514 /* Charge nothing, and allocate a mapped subsection */
2515 PagedCharge = 0;
2516 Size = sizeof(MSUBSECTION);
2517 }
2518
2519 /* Check if this is a normal CA */
2520 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2521 ASSERT(ControlArea->u.Flags.Rom == 0);
2522
2523 /* Charge only a CA, and the subsection is right after */
2524 NonPagedCharge = sizeof(CONTROL_AREA);
2525 Subsection = (PSUBSECTION)(ControlArea + 1);
2526
2527 /* We only support single-subsection mappings */
2528 NonPagedCharge += Size;
2529 ASSERT(Subsection->NextSubsection == NULL);
2530
2531 /* Create the actual section object, with enough space for the prototype PTEs */
2532 Status = ObCreateObject(PreviousMode,
2533 MmSectionObjectType,
2534 ObjectAttributes,
2535 PreviousMode,
2536 NULL,
2537 sizeof(SECTION),
2538 PagedCharge,
2539 NonPagedCharge,
2540 (PVOID*)&NewSection);
2541 ASSERT(NT_SUCCESS(Status));
2542
2543 /* Now copy the local section object from the stack into this new object */
2544 RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
2545 NewSection->Address.StartingVpn = 0;
2546
2547 /* For now, only user calls are supported */
2548 ASSERT(KernelCall == FALSE);
2549 NewSection->u.Flags.UserReference = TRUE;
2550
2551 /* Migrate the attribute into a flag */
2552 if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE;
2553
2554 /* If R/W access is not requested, this might eventually become a CoW mapping */
2555 if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
2556 {
2557 NewSection->u.Flags.CopyOnWrite = TRUE;
2558 }
2559
2560 /* Is this a "based" allocation, in which all mappings are identical? */
2561 if (AllocationAttributes & SEC_BASED)
2562 {
2563 /* Convert the flag, and make sure the section isn't too big */
2564 NewSection->u.Flags.Based = TRUE;
2565 if ((ULONGLONG)NewSection->SizeOfSection.QuadPart >
2566 (ULONG_PTR)MmHighSectionBase)
2567 {
2568 DPRINT1("BASED section is too large\n");
2569 ObDereferenceObject(NewSection);
2570 return STATUS_NO_MEMORY;
2571 }
2572
2573 /* Lock the VAD tree during the search */
2574 KeAcquireGuardedMutex(&MmSectionBasedMutex);
2575
2576 /* Find an address top-down */
2577 Status = MiFindEmptyAddressRangeDownBasedTree(NewSection->SizeOfSection.LowPart,
2578 (ULONG_PTR)MmHighSectionBase,
2579 _64K,
2580 &MmSectionBasedRoot,
2581 &NewSection->Address.StartingVpn);
2582 ASSERT(NT_SUCCESS(Status));
2583
2584 /* Compute the ending address and insert it into the VAD tree */
2585 NewSection->Address.EndingVpn = NewSection->Address.StartingVpn +
2586 NewSection->SizeOfSection.LowPart -
2587 1;
2588 MiInsertBasedSection(NewSection);
2589
2590 /* Finally release the lock */
2591 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2592 }
2593
2594 /* Write down if this was a kernel call */
2595 ControlArea->u.Flags.WasPurged |= KernelCall;
2596 ASSERT(ControlArea->u.Flags.WasPurged == FALSE);
2597
2598 /* Make sure the segment and the section are the same size, or the section is smaller */
2599 ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment);
2600
2601 /* Return the object and the creation status */
2602 *SectionObject = (PVOID)NewSection;
2603 return Status;
2604 }
2605
2606 /*
2607 * @implemented
2608 */
2609 NTSTATUS
2610 NTAPI
2611 MmMapViewOfArm3Section(IN PVOID SectionObject,
2612 IN PEPROCESS Process,
2613 IN OUT PVOID *BaseAddress,
2614 IN ULONG_PTR ZeroBits,
2615 IN SIZE_T CommitSize,
2616 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
2617 IN OUT PSIZE_T ViewSize,
2618 IN SECTION_INHERIT InheritDisposition,
2619 IN ULONG AllocationType,
2620 IN ULONG Protect)
2621 {
2622 KAPC_STATE ApcState;
2623 BOOLEAN Attached = FALSE;
2624 PSECTION Section;
2625 PCONTROL_AREA ControlArea;
2626 ULONG ProtectionMask;
2627 NTSTATUS Status;
2628 PAGED_CODE();
2629
2630 /* Get the segment and control area */
2631 Section = (PSECTION)SectionObject;
2632 ControlArea = Section->Segment->ControlArea;
2633
2634 /* These flags/states are not yet supported by ARM3 */
2635 ASSERT(Section->u.Flags.Image == 0);
2636 ASSERT(Section->u.Flags.NoCache == 0);
2637 ASSERT(Section->u.Flags.WriteCombined == 0);
2638 ASSERT((AllocationType & MEM_RESERVE) == 0);
2639 ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2640
2641 /* Check if the mapping protection is compatible with the create */
2642 if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2643 {
2644 DPRINT1("Mapping protection is incompatible\n");
2645 return STATUS_SECTION_PROTECTION;
2646 }
2647
2648 /* Check if the offset and size would cause an overflow */
2649 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2650 (ULONG64)SectionOffset->QuadPart)
2651 {
2652 DPRINT1("Section offset overflows\n");
2653 return STATUS_INVALID_VIEW_SIZE;
2654 }
2655
2656 /* Check if the offset and size are bigger than the section itself */
2657 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2658 (ULONG64)Section->SizeOfSection.QuadPart)
2659 {
2660 DPRINT1("Section offset is larger than section\n");
2661 return STATUS_INVALID_VIEW_SIZE;
2662 }
2663
2664 /* Check if the caller did not specify a view size */
2665 if (!(*ViewSize))
2666 {
2667 /* Compute it for the caller */
2668 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
2669
2670 /* Check if it's larger than 4GB or overflows into kernel-mode */
2671 if ((*ViewSize > 0xFFFFFFFF) ||
2672 (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize))
2673 {
2674 DPRINT1("Section view won't fit\n");
2675 return STATUS_INVALID_VIEW_SIZE;
2676 }
2677 }
2678
2679 /* Check if the commit size is larger than the view size */
2680 if (CommitSize > *ViewSize)
2681 {
2682 DPRINT1("Attempting to commit more than the view itself\n");
2683 return STATUS_INVALID_PARAMETER_5;
2684 }
2685
2686 /* Check if the view size is larger than the section */
2687 if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2688 {
2689 DPRINT1("The view is larger than the section\n");
2690 return STATUS_INVALID_VIEW_SIZE;
2691 }
2692
2693 /* Compute and validate the protection mask */
2694 ProtectionMask = MiMakeProtectionMask(Protect);
2695 if (ProtectionMask == MM_INVALID_PROTECTION)
2696 {
2697 DPRINT1("The protection is invalid\n");
2698 return STATUS_INVALID_PAGE_PROTECTION;
2699 }
2700
2701 /* We only handle pagefile-backed sections, which cannot be writecombined */
2702 if (Protect & PAGE_WRITECOMBINE)
2703 {
2704 DPRINT1("Cannot write combine a pagefile-backed section\n");
2705 return STATUS_INVALID_PARAMETER_10;
2706 }
2707
2708 /* Start by attaching to the current process if needed */
2709 if (PsGetCurrentProcess() != Process)
2710 {
2711 KeStackAttachProcess(&Process->Pcb, &ApcState);
2712 Attached = TRUE;
2713 }
2714
2715 /* Lock the address space and make sure the process is alive */
2716 MmLockAddressSpace(&Process->Vm);
2717 if (!Process->VmDeleted)
2718 {
2719 /* Do the actual mapping */
2720 DPRINT("Mapping ARM3 data section\n");
2721 Status = MiMapViewOfDataSection(ControlArea,
2722 Process,
2723 BaseAddress,
2724 SectionOffset,
2725 ViewSize,
2726 Section,
2727 InheritDisposition,
2728 ProtectionMask,
2729 CommitSize,
2730 ZeroBits,
2731 AllocationType);
2732 }
2733 else
2734 {
2735 /* The process is being terminated, fail */
2736 DPRINT1("The process is dying\n");
2737 Status = STATUS_PROCESS_IS_TERMINATING;
2738 }
2739
2740 /* Unlock the address space and detatch if needed, then return status */
2741 MmUnlockAddressSpace(&Process->Vm);
2742 if (Attached) KeUnstackDetachProcess(&ApcState);
2743 return Status;
2744 }
2745
2746 /*
2747 * @unimplemented
2748 */
2749 BOOLEAN
2750 NTAPI
2751 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2752 {
2753 UNIMPLEMENTED;
2754 return FALSE;
2755 }
2756
2757 /*
2758 * @unimplemented
2759 */
2760 BOOLEAN
2761 NTAPI
2762 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2763 IN BOOLEAN DelayClose)
2764 {
2765 UNIMPLEMENTED;
2766 return FALSE;
2767 }
2768
2769 /*
2770 * @implemented
2771 */
2772 NTSTATUS
2773 NTAPI
2774 MmMapViewInSessionSpace(IN PVOID Section,
2775 OUT PVOID *MappedBase,
2776 IN OUT PSIZE_T ViewSize)
2777 {
2778 PAGED_CODE();
2779
2780 // HACK
2781 if (MiIsRosSectionObject(Section))
2782 {
2783 return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
2784 }
2785
2786 /* Process must be in a session */
2787 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2788 {
2789 DPRINT1("Process is not in session\n");
2790 return STATUS_NOT_MAPPED_VIEW;
2791 }
2792
2793 /* Use the system space API, but with the session view instead */
2794 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2795 return MiMapViewInSystemSpace(Section,
2796 &MmSessionSpace->Session,
2797 MappedBase,
2798 ViewSize);
2799 }
2800
2801 /*
2802 * @implemented
2803 */
2804 NTSTATUS
2805 NTAPI
2806 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
2807 {
2808 PAGED_CODE();
2809
2810 // HACK
2811 if (!MI_IS_SESSION_ADDRESS(MappedBase))
2812 {
2813 return MmUnmapViewInSystemSpace(MappedBase);
2814 }
2815
2816 /* Process must be in a session */
2817 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2818 {
2819 DPRINT1("Proess is not in session\n");
2820 return STATUS_NOT_MAPPED_VIEW;
2821 }
2822
2823 /* Use the system space API, but with the session view instead */
2824 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2825 return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
2826 MappedBase);
2827 }
2828
2829 /*
2830 * @implemented
2831 */
2832 NTSTATUS
2833 NTAPI
2834 MmUnmapViewOfSection(IN PEPROCESS Process,
2835 IN PVOID BaseAddress)
2836 {
2837 return MiUnmapViewOfSection(Process, BaseAddress, 0);
2838 }
2839
2840 /*
2841 * @implemented
2842 */
2843 NTSTATUS
2844 NTAPI
2845 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
2846 {
2847 PMEMORY_AREA MemoryArea;
2848 PAGED_CODE();
2849
2850 /* Was this mapped by RosMm? */
2851 MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
2852 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
2853 {
2854 return MiRosUnmapViewInSystemSpace(MappedBase);
2855 }
2856
2857 /* It was not, call the ARM3 routine */
2858 return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
2859 }
2860
2861 /*
2862 * @implemented
2863 */
2864 NTSTATUS
2865 NTAPI
2866 MmCommitSessionMappedView(IN PVOID MappedBase,
2867 IN SIZE_T ViewSize)
2868 {
2869 ULONG_PTR StartAddress, EndingAddress, Base;
2870 ULONG Hash, Count = 0, Size, QuotaCharge;
2871 PMMSESSION Session;
2872 PMMPTE LastProtoPte, PointerPte, ProtoPte;
2873 PCONTROL_AREA ControlArea;
2874 PSEGMENT Segment;
2875 PSUBSECTION Subsection;
2876 MMPTE TempPte;
2877 PAGED_CODE();
2878
2879 /* Make sure the base isn't past the session view range */
2880 if ((MappedBase < MiSessionViewStart) ||
2881 (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
2882 {
2883 DPRINT1("Base outside of valid range\n");
2884 return STATUS_INVALID_PARAMETER_1;
2885 }
2886
2887 /* Make sure the size isn't past the session view range */
2888 if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
2889 (ULONG_PTR)MappedBase) < ViewSize)
2890 {
2891 DPRINT1("Size outside of valid range\n");
2892 return STATUS_INVALID_PARAMETER_2;
2893 }
2894
2895 /* Sanity check */
2896 ASSERT(ViewSize != 0);
2897
2898 /* Process must be in a session */
2899 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2900 {
2901 DPRINT1("Process is not in session\n");
2902 return STATUS_NOT_MAPPED_VIEW;
2903 }
2904
2905 /* Compute the correctly aligned base and end addresses */
2906 StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
2907 EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
2908
2909 /* Sanity check and grab the session */
2910 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2911 Session = &MmSessionSpace->Session;
2912
2913 /* Get the hash entry for this allocation */
2914 Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
2915
2916 /* Lock system space */
2917 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2918
2919 /* Loop twice so we can try rolling over if needed */
2920 while (TRUE)
2921 {
2922 /* Extract the size and base addresses from the entry */
2923 Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
2924 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2925
2926 /* Convert the size to bucket chunks */
2927 Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
2928
2929 /* Bail out if this entry fits in here */
2930 if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
2931
2932 /* Check if we overflew past the end of the hash table */
2933 if (++Hash >= Session->SystemSpaceHashSize)
2934 {
2935 /* Reset the hash to zero and keep searching from the bottom */
2936 Hash = 0;
2937 if (++Count == 2)
2938 {
2939 /* But if we overflew twice, then this is not a real mapping */
2940 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2941 Base,
2942 2,
2943 0,
2944 0);
2945 }
2946 }
2947 }
2948
2949 /* Make sure the view being mapped is not file-based */
2950 ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2951 if (ControlArea->FilePointer != NULL)
2952 {
2953 /* It is, so we have to bail out */
2954 DPRINT1("Only page-filed backed sections can be commited\n");
2955 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2956 return STATUS_ALREADY_COMMITTED;
2957 }
2958
2959 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
2960 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2961 ASSERT(ControlArea->u.Flags.Rom == 0);
2962 Subsection = (PSUBSECTION)(ControlArea + 1);
2963
2964 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
2965 ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
2966 QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
2967 LastProtoPte = ProtoPte + QuotaCharge;
2968 if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
2969 {
2970 DPRINT1("PTE is out of bounds\n");
2971 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2972 return STATUS_INVALID_PARAMETER_2;
2973 }
2974
2975 /* Acquire the commit lock and count all the non-committed PTEs */
2976 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
2977 PointerPte = ProtoPte;
2978 while (PointerPte < LastProtoPte)
2979 {
2980 if (PointerPte->u.Long) QuotaCharge--;
2981 PointerPte++;
2982 }
2983
2984 /* Was everything committed already? */
2985 if (!QuotaCharge)
2986 {
2987 /* Nothing to do! */
2988 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
2989 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2990 return STATUS_SUCCESS;
2991 }
2992
2993 /* Pick the segment and template PTE */
2994 Segment = ControlArea->Segment;
2995 TempPte = Segment->SegmentPteTemplate;
2996 ASSERT(TempPte.u.Long != 0);
2997
2998 /* Loop all prototype PTEs to be committed */
2999 PointerPte = ProtoPte;
3000 while (PointerPte < LastProtoPte)
3001 {
3002 /* Make sure the PTE is already invalid */
3003 if (PointerPte->u.Long == 0)
3004 {
3005 /* And write the invalid PTE */
3006 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3007 }
3008
3009 /* Move to the next PTE */
3010 PointerPte++;
3011 }
3012
3013 /* Check if we had at least one page charged */
3014 if (QuotaCharge)
3015 {
3016 /* Update the accounting data */
3017 Segment->NumberOfCommittedPages += QuotaCharge;
3018 InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3019 }
3020
3021 /* Release all */
3022 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3023 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3024 return STATUS_SUCCESS;
3025 }
3026
3027 /* SYSTEM CALLS ***************************************************************/
3028
3029 NTSTATUS
3030 NTAPI
3031 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3032 IN PVOID File2MappedAsFile)
3033 {
3034 PVOID AddressSpace;
3035 PMEMORY_AREA MemoryArea1, MemoryArea2;
3036 PROS_SECTION_OBJECT Section1, Section2;
3037
3038 /* Lock address space */
3039 AddressSpace = MmGetCurrentAddressSpace();
3040 MmLockAddressSpace(AddressSpace);
3041
3042 /* Locate the memory area for the process by address */
3043 MemoryArea1 = MmLocateMemoryAreaByAddress(AddressSpace, File1MappedAsAnImage);
3044 if (!MemoryArea1)
3045 {
3046 /* Fail, the address does not exist */
3047 MmUnlockAddressSpace(AddressSpace);
3048 return STATUS_INVALID_ADDRESS;
3049 }
3050
3051 /* Check if it's a section view (RosMm section) or ARM3 section */
3052 if (MemoryArea1->Type != MEMORY_AREA_SECTION_VIEW)
3053 {
3054 /* Fail, the address is not a section */
3055 MmUnlockAddressSpace(AddressSpace);
3056 return STATUS_CONFLICTING_ADDRESSES;
3057 }
3058
3059 /* Get the section pointer to the SECTION_OBJECT */
3060 Section1 = MemoryArea1->Data.SectionData.Section;
3061 if (Section1->FileObject == NULL)
3062 {
3063 MmUnlockAddressSpace(AddressSpace);
3064 return STATUS_CONFLICTING_ADDRESSES;
3065 }
3066
3067 /* Locate the memory area for the process by address */
3068 MemoryArea2 = MmLocateMemoryAreaByAddress(AddressSpace, File2MappedAsFile);
3069 if (!MemoryArea2)
3070 {
3071 /* Fail, the address does not exist */
3072 MmUnlockAddressSpace(AddressSpace);
3073 return STATUS_INVALID_ADDRESS;
3074 }
3075
3076 /* Check if it's a section view (RosMm section) or ARM3 section */
3077 if (MemoryArea2->Type != MEMORY_AREA_SECTION_VIEW)
3078 {
3079 /* Fail, the address is not a section */
3080 MmUnlockAddressSpace(AddressSpace);
3081 return STATUS_CONFLICTING_ADDRESSES;
3082 }
3083
3084 /* Get the section pointer to the SECTION_OBJECT */
3085 Section2 = MemoryArea2->Data.SectionData.Section;
3086 if (Section2->FileObject == NULL)
3087 {
3088 MmUnlockAddressSpace(AddressSpace);
3089 return STATUS_CONFLICTING_ADDRESSES;
3090 }
3091
3092 /* The shared cache map seems to be the same if both of these are equal */
3093 if (Section1->FileObject->SectionObjectPointer->SharedCacheMap ==
3094 Section2->FileObject->SectionObjectPointer->SharedCacheMap)
3095 {
3096 MmUnlockAddressSpace(AddressSpace);
3097 return STATUS_SUCCESS;
3098 }
3099
3100 /* Unlock address space */
3101 MmUnlockAddressSpace(AddressSpace);
3102 return STATUS_NOT_SAME_DEVICE;
3103 }
3104
3105 /*
3106 * @implemented
3107 */
3108 NTSTATUS
3109 NTAPI
3110 NtCreateSection(OUT PHANDLE SectionHandle,
3111 IN ACCESS_MASK DesiredAccess,
3112 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3113 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3114 IN ULONG SectionPageProtection OPTIONAL,
3115 IN ULONG AllocationAttributes,
3116 IN HANDLE FileHandle OPTIONAL)
3117 {
3118 LARGE_INTEGER SafeMaximumSize;
3119 PVOID SectionObject;
3120 HANDLE Handle;
3121 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3122 NTSTATUS Status;
3123 PAGED_CODE();
3124
3125 /* Check for non-existing flags */
3126 if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
3127 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
3128 SEC_NO_CHANGE)))
3129 {
3130 if (!(AllocationAttributes & 1))
3131 {
3132 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
3133 return STATUS_INVALID_PARAMETER_6;
3134 }
3135 }
3136
3137 /* Check for no allocation type */
3138 if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
3139 {
3140 DPRINT1("Missing allocation type in allocation attributes\n");
3141 return STATUS_INVALID_PARAMETER_6;
3142 }
3143
3144 /* Check for image allocation with invalid attributes */
3145 if ((AllocationAttributes & SEC_IMAGE) &&
3146 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
3147 SEC_NOCACHE | SEC_NO_CHANGE)))
3148 {
3149 DPRINT1("Image allocation with invalid attributes\n");
3150 return STATUS_INVALID_PARAMETER_6;
3151 }
3152
3153 /* Check for allocation type is both commit and reserve */
3154 if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
3155 {
3156 DPRINT1("Commit and reserve in the same time\n");
3157 return STATUS_INVALID_PARAMETER_6;
3158 }
3159
3160 /* Now check for valid protection */
3161 if ((SectionPageProtection & PAGE_NOCACHE) ||
3162 (SectionPageProtection & PAGE_WRITECOMBINE) ||
3163 (SectionPageProtection & PAGE_GUARD) ||
3164 (SectionPageProtection & PAGE_NOACCESS))
3165 {
3166 DPRINT1("Sections don't support these protections\n");
3167 return STATUS_INVALID_PAGE_PROTECTION;
3168 }
3169
3170 /* Use a maximum size of zero, if none was specified */
3171 SafeMaximumSize.QuadPart = 0;
3172
3173 /* Check for user-mode caller */
3174 if (PreviousMode != KernelMode)
3175 {
3176 /* Enter SEH */
3177 _SEH2_TRY
3178 {
3179 /* Safely check user-mode parameters */
3180 if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3181 MaximumSize = &SafeMaximumSize;
3182 ProbeForWriteHandle(SectionHandle);
3183 }
3184 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3185 {
3186 /* Return the exception code */
3187 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3188 }
3189 _SEH2_END;
3190 }
3191 else if (!MaximumSize) MaximumSize = &SafeMaximumSize;
3192
3193 /* Check that MaximumSize is valid if backed by paging file */
3194 if ((!FileHandle) && (!MaximumSize->QuadPart))
3195 return STATUS_INVALID_PARAMETER_4;
3196
3197 /* Create the section */
3198 Status = MmCreateSection(&SectionObject,
3199 DesiredAccess,
3200 ObjectAttributes,
3201 MaximumSize,
3202 SectionPageProtection,
3203 AllocationAttributes,
3204 FileHandle,
3205 NULL);
3206 if (!NT_SUCCESS(Status)) return Status;
3207
3208 /* FIXME: Should zero last page for a file mapping */
3209
3210 /* Now insert the object */
3211 Status = ObInsertObject(SectionObject,
3212 NULL,
3213 DesiredAccess,
3214 0,
3215 NULL,
3216 &Handle);
3217 if (NT_SUCCESS(Status))
3218 {
3219 /* Enter SEH */
3220 _SEH2_TRY
3221 {
3222 /* Return the handle safely */
3223 *SectionHandle = Handle;
3224 }
3225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3226 {
3227 /* Nothing here */
3228 }
3229 _SEH2_END;
3230 }
3231
3232 /* Return the status */
3233 return Status;
3234 }
3235
3236 NTSTATUS
3237 NTAPI
3238 NtOpenSection(OUT PHANDLE SectionHandle,
3239 IN ACCESS_MASK DesiredAccess,
3240 IN POBJECT_ATTRIBUTES ObjectAttributes)
3241 {
3242 HANDLE Handle;
3243 NTSTATUS Status;
3244 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3245 PAGED_CODE();
3246
3247 /* Check for user-mode caller */
3248 if (PreviousMode != KernelMode)
3249 {
3250 /* Enter SEH */
3251 _SEH2_TRY
3252 {
3253 /* Safely check user-mode parameters */
3254 ProbeForWriteHandle(SectionHandle);
3255 }
3256 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3257 {
3258 /* Return the exception code */
3259 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3260 }
3261 _SEH2_END;
3262 }
3263
3264 /* Try opening the object */
3265 Status = ObOpenObjectByName(ObjectAttributes,
3266 MmSectionObjectType,
3267 PreviousMode,
3268 NULL,
3269 DesiredAccess,
3270 NULL,
3271 &Handle);
3272
3273 /* Enter SEH */
3274 _SEH2_TRY
3275 {
3276 /* Return the handle safely */
3277 *SectionHandle = Handle;
3278 }
3279 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3280 {
3281 /* Nothing here */
3282 }
3283 _SEH2_END;
3284
3285 /* Return the status */
3286 return Status;
3287 }
3288
3289 NTSTATUS
3290 NTAPI
3291 NtMapViewOfSection(IN HANDLE SectionHandle,
3292 IN HANDLE ProcessHandle,
3293 IN OUT PVOID* BaseAddress,
3294 IN ULONG_PTR ZeroBits,
3295 IN SIZE_T CommitSize,
3296 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3297 IN OUT PSIZE_T ViewSize,
3298 IN SECTION_INHERIT InheritDisposition,
3299 IN ULONG AllocationType,
3300 IN ULONG Protect)
3301 {
3302 PVOID SafeBaseAddress;
3303 LARGE_INTEGER SafeSectionOffset;
3304 SIZE_T SafeViewSize;
3305 PROS_SECTION_OBJECT Section;
3306 PEPROCESS Process;
3307 NTSTATUS Status;
3308 ACCESS_MASK DesiredAccess;
3309 ULONG ProtectionMask;
3310 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3311
3312 /* Check for invalid zero bits */
3313 if (ZeroBits > 21) // per-arch?
3314 {
3315 DPRINT1("Invalid zero bits\n");
3316 return STATUS_INVALID_PARAMETER_4;
3317 }
3318
3319 /* Check for invalid inherit disposition */
3320 if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare))
3321 {
3322 DPRINT1("Invalid inherit disposition\n");
3323 return STATUS_INVALID_PARAMETER_8;
3324 }
3325
3326 /* Allow only valid allocation types */
3327 if ((AllocationType & ~(MEM_TOP_DOWN | MEM_LARGE_PAGES | MEM_DOS_LIM |
3328 SEC_NO_CHANGE | MEM_RESERVE)))
3329 {
3330 DPRINT1("Invalid allocation type\n");
3331 return STATUS_INVALID_PARAMETER_9;
3332 }
3333
3334 /* Convert the protection mask, and validate it */
3335 ProtectionMask = MiMakeProtectionMask(Protect);
3336 if (ProtectionMask == MM_INVALID_PROTECTION)
3337 {
3338 DPRINT1("Invalid page protection\n");
3339 return STATUS_INVALID_PAGE_PROTECTION;
3340 }
3341
3342 /* Check for non-allocation-granularity-aligned BaseAddress */
3343 if (BaseAddress && (*BaseAddress != ALIGN_DOWN_POINTER_BY(*BaseAddress, MM_VIRTMEM_GRANULARITY)))
3344 {
3345 DPRINT("BaseAddress is not at 64-kilobyte address boundary.");
3346 return STATUS_MAPPED_ALIGNMENT;
3347 }
3348
3349 /* Now convert the protection mask into desired section access mask */
3350 DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
3351
3352 /* Assume no section offset */
3353 SafeSectionOffset.QuadPart = 0;
3354
3355 /* Enter SEH */
3356 _SEH2_TRY
3357 {
3358 /* Check for unsafe parameters */
3359 if (PreviousMode != KernelMode)
3360 {
3361 /* Probe the parameters */
3362 ProbeForWritePointer(BaseAddress);
3363 ProbeForWriteSize_t(ViewSize);
3364 }
3365
3366 /* Check if a section offset was given */
3367 if (SectionOffset)
3368 {
3369 /* Check for unsafe parameters and capture section offset */
3370 if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset);
3371 SafeSectionOffset = *SectionOffset;
3372 }
3373
3374 /* Capture the other parameters */
3375 SafeBaseAddress = *BaseAddress;
3376 SafeViewSize = *ViewSize;
3377 }
3378 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3379 {
3380 /* Return the exception code */
3381 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3382 }
3383 _SEH2_END;
3384
3385 /* Check for kernel-mode address */
3386 if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS)
3387 {
3388 DPRINT1("Kernel base not allowed\n");
3389 return STATUS_INVALID_PARAMETER_3;
3390 }
3391
3392 /* Check for range entering kernel-mode */
3393 if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize)
3394 {
3395 DPRINT1("Overflowing into kernel base not allowed\n");
3396 return STATUS_INVALID_PARAMETER_3;
3397 }
3398
3399 /* Check for invalid zero bits */
3400 if (((ULONG_PTR)SafeBaseAddress + SafeViewSize) > (0xFFFFFFFF >> ZeroBits)) // arch?
3401 {
3402 DPRINT1("Invalid zero bits\n");
3403 return STATUS_INVALID_PARAMETER_4;
3404 }
3405
3406 /* Reference the process */
3407 Status = ObReferenceObjectByHandle(ProcessHandle,
3408 PROCESS_VM_OPERATION,
3409 PsProcessType,
3410 PreviousMode,
3411 (PVOID*)&Process,
3412 NULL);
3413 if (!NT_SUCCESS(Status)) return Status;
3414
3415 /* Reference the section */
3416 Status = ObReferenceObjectByHandle(SectionHandle,
3417 DesiredAccess,
3418 MmSectionObjectType,
3419 PreviousMode,
3420 (PVOID*)&Section,
3421 NULL);
3422 if (!NT_SUCCESS(Status))
3423 {
3424 ObDereferenceObject(Process);
3425 return Status;
3426 }
3427
3428 /* Now do the actual mapping */
3429 Status = MmMapViewOfSection(Section,
3430 Process,
3431 &SafeBaseAddress,
3432 ZeroBits,
3433 CommitSize,
3434 &SafeSectionOffset,
3435 &SafeViewSize,
3436 InheritDisposition,
3437 AllocationType,
3438 Protect);
3439
3440 /* Return data only on success */
3441 if (NT_SUCCESS(Status))
3442 {
3443 /* Check if this is an image for the current process */
3444 if ((Section->AllocationAttributes & SEC_IMAGE) &&
3445 (Process == PsGetCurrentProcess()) &&
3446 (Status != STATUS_IMAGE_NOT_AT_BASE))
3447 {
3448 /* Notify the debugger */
3449 DbgkMapViewOfSection(Section,
3450 SafeBaseAddress,
3451 SafeSectionOffset.LowPart,
3452 SafeViewSize);
3453 }
3454
3455 /* Enter SEH */
3456 _SEH2_TRY
3457 {
3458 /* Return parameters to user */
3459 *BaseAddress = SafeBaseAddress;
3460 *ViewSize = SafeViewSize;
3461 if (SectionOffset) *SectionOffset = SafeSectionOffset;
3462 }
3463 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3464 {
3465 /* Nothing to do */
3466 }
3467 _SEH2_END;
3468 }
3469
3470 /* Dereference all objects and return status */
3471 ObDereferenceObject(Section);
3472 ObDereferenceObject(Process);
3473 return Status;
3474 }
3475
3476 NTSTATUS
3477 NTAPI
3478 NtUnmapViewOfSection(IN HANDLE ProcessHandle,
3479 IN PVOID BaseAddress)
3480 {
3481 PEPROCESS Process;
3482 NTSTATUS Status;
3483 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3484
3485 /* Don't allowing mapping kernel views */
3486 if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS))
3487 {
3488 DPRINT1("Trying to unmap a kernel view\n");
3489 return STATUS_NOT_MAPPED_VIEW;
3490 }
3491
3492 /* Reference the process */
3493 Status = ObReferenceObjectByHandle(ProcessHandle,
3494 PROCESS_VM_OPERATION,
3495 PsProcessType,
3496 PreviousMode,
3497 (PVOID*)&Process,
3498 NULL);
3499 if (!NT_SUCCESS(Status)) return Status;
3500
3501 /* Unmap the view */
3502 Status = MiUnmapViewOfSection(Process, BaseAddress, 0);
3503
3504 /* Dereference the process and return status */
3505 ObDereferenceObject(Process);
3506 return Status;
3507 }
3508
3509 NTSTATUS
3510 NTAPI
3511 NtExtendSection(IN HANDLE SectionHandle,
3512 IN OUT PLARGE_INTEGER NewMaximumSize)
3513 {
3514 LARGE_INTEGER SafeNewMaximumSize;
3515 PROS_SECTION_OBJECT Section;
3516 NTSTATUS Status;
3517 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3518
3519 /* Check for user-mode parameters */
3520 if (PreviousMode != KernelMode)
3521 {
3522 /* Enter SEH */
3523 _SEH2_TRY
3524 {
3525 /* Probe and capture the maximum size, it's both read and write */
3526 ProbeForWriteLargeInteger(NewMaximumSize);
3527 SafeNewMaximumSize = *NewMaximumSize;
3528 }
3529 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3530 {
3531 /* Return the exception code */
3532 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3533 }
3534 _SEH2_END;
3535 }
3536 else
3537 {
3538 /* Just read the size directly */
3539 SafeNewMaximumSize = *NewMaximumSize;
3540 }
3541
3542 /* Reference the section */
3543 Status = ObReferenceObjectByHandle(SectionHandle,
3544 SECTION_EXTEND_SIZE,
3545 MmSectionObjectType,
3546 PreviousMode,
3547 (PVOID*)&Section,
3548 NULL);
3549 if (!NT_SUCCESS(Status)) return Status;
3550
3551 /* Really this should go in MmExtendSection */
3552 if (!(Section->AllocationAttributes & SEC_FILE))
3553 {
3554 DPRINT1("Not extending a file\n");
3555 ObDereferenceObject(Section);
3556 return STATUS_SECTION_NOT_EXTENDED;
3557 }
3558
3559 /* FIXME: Do the work */
3560
3561 /* Dereference the section */
3562 ObDereferenceObject(Section);
3563
3564 /* Enter SEH */
3565 _SEH2_TRY
3566 {
3567 /* Write back the new size */
3568 *NewMaximumSize = SafeNewMaximumSize;
3569 }
3570 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3571 {
3572 /* Nothing to do */
3573 }
3574 _SEH2_END;
3575
3576 /* Return the status */
3577 return STATUS_NOT_IMPLEMENTED;
3578 }
3579
3580 /* EOF */