Revert a change from the previous version. MI_WRITE_VALID_PTE only works on invalid...
[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_DECOMMIT;
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 ASSERT(PointerPte->u.Hard.Valid == 1);
1918 ASSERT(TempPte.u.Hard.Valid == 1);
1919 ASSERT(PointerPte->u.Hard.PageFrameNumber == TempPte.u.Hard.PageFrameNumber);
1920 *PointerPte = TempPte;
1921
1922 //
1923 // Flush the TLB
1924 //
1925 ASSERT(PreviousPte.u.Hard.Valid == 1);
1926 KeFlushCurrentTb();
1927 ASSERT(PreviousPte.u.Hard.Valid == 1);
1928
1929 //
1930 // Windows updates the relevant PFN1 information, we currently don't.
1931 //
1932 if (CaptureDirtyBit) DPRINT1("Warning, not handling dirty bit\n");
1933
1934 //
1935 // Not supported in ARM3
1936 //
1937 ASSERT(FoundVad->u.VadFlags.VadType != VadWriteWatch);
1938
1939 //
1940 // Release the PFN lock, we are done
1941 //
1942 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1943 }
1944
1945 //
1946 // NOTE: This function gets a lot more complicated if we want Copy-on-Write support
1947 //
1948 NTSTATUS
1949 NTAPI
1950 MiSetProtectionOnSection(IN PEPROCESS Process,
1951 IN PMMVAD FoundVad,
1952 IN PVOID StartingAddress,
1953 IN PVOID EndingAddress,
1954 IN ULONG NewProtect,
1955 OUT PULONG CapturedOldProtect,
1956 IN ULONG DontCharge,
1957 OUT PULONG Locked)
1958 {
1959 PMMPTE PointerPte, LastPte;
1960 MMPTE TempPte, PteContents;
1961 PMMPDE PointerPde;
1962 PMMPFN Pfn1;
1963 ULONG ProtectionMask, QuotaCharge = 0;
1964 PETHREAD Thread = PsGetCurrentThread();
1965 PAGED_CODE();
1966
1967 //
1968 // Tell caller nothing is being locked
1969 //
1970 *Locked = FALSE;
1971
1972 //
1973 // This function should only be used for section VADs. Windows ASSERT */
1974 //
1975 ASSERT(FoundVad->u.VadFlags.PrivateMemory == 0);
1976
1977 //
1978 // We don't support these features in ARM3
1979 //
1980 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
1981 ASSERT(FoundVad->u2.VadFlags2.CopyOnWrite == 0);
1982
1983 //
1984 // Convert and validate the protection mask
1985 //
1986 ProtectionMask = MiMakeProtectionMask(NewProtect);
1987 if (ProtectionMask == MM_INVALID_PROTECTION)
1988 {
1989 DPRINT1("Invalid section protect\n");
1990 return STATUS_INVALID_PAGE_PROTECTION;
1991 }
1992
1993 //
1994 // Get the PTE and PDE for the address, as well as the final PTE
1995 //
1996 MiLockProcessWorkingSetUnsafe(Process, Thread);
1997 PointerPde = MiAddressToPde(StartingAddress);
1998 PointerPte = MiAddressToPte(StartingAddress);
1999 LastPte = MiAddressToPte(EndingAddress);
2000
2001 //
2002 // Make the PDE valid, and check the status of the first PTE
2003 //
2004 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2005 if (PointerPte->u.Long)
2006 {
2007 //
2008 // Not supported in ARM3
2009 //
2010 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical);
2011
2012 //
2013 // Capture the page protection and make the PDE valid
2014 //
2015 *CapturedOldProtect = MiGetPageProtection(PointerPte);
2016 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2017 }
2018 else
2019 {
2020 //
2021 // Only pagefile-backed section VADs are supported for now
2022 //
2023 ASSERT(FoundVad->u.VadFlags.VadType != VadImageMap);
2024
2025 //
2026 // Grab the old protection from the VAD itself
2027 //
2028 *CapturedOldProtect = MmProtectToValue[FoundVad->u.VadFlags.Protection];
2029 }
2030
2031 //
2032 // Loop all the PTEs now
2033 //
2034 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2035 while (PointerPte <= LastPte)
2036 {
2037 //
2038 // Check if we've crossed a PDE boundary and make the new PDE valid too
2039 //
2040 if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
2041 {
2042 PointerPde = MiAddressToPte(PointerPte);
2043 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
2044 }
2045
2046 //
2047 // Capture the PTE and see what we're dealing with
2048 //
2049 PteContents = *PointerPte;
2050 if (PteContents.u.Long == 0)
2051 {
2052 //
2053 // This used to be a zero PTE and it no longer is, so we must add a
2054 // reference to the pagetable.
2055 //
2056 MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
2057
2058 //
2059 // Create the demand-zero prototype PTE
2060 //
2061 TempPte = PrototypePte;
2062 TempPte.u.Soft.Protection = ProtectionMask;
2063 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
2064 }
2065 else if (PteContents.u.Hard.Valid == 1)
2066 {
2067 //
2068 // Get the PFN entry
2069 //
2070 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2071
2072 //
2073 // We don't support these yet
2074 //
2075 ASSERT((NewProtect & (PAGE_NOACCESS | PAGE_GUARD)) == 0);
2076 ASSERT(Pfn1->u3.e1.PrototypePte == 0);
2077
2078 //
2079 // Write the protection mask and write it with a TLB flush
2080 //
2081 Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
2082 MiFlushTbAndCapture(FoundVad,
2083 PointerPte,
2084 ProtectionMask,
2085 Pfn1,
2086 TRUE);
2087 }
2088 else
2089 {
2090 //
2091 // We don't support these cases yet
2092 //
2093 ASSERT(PteContents.u.Soft.Prototype == 0);
2094 ASSERT(PteContents.u.Soft.Transition == 0);
2095
2096 //
2097 // The PTE is already demand-zero, just update the protection mask
2098 //
2099 PointerPte->u.Soft.Protection = ProtectionMask;
2100 }
2101
2102 PointerPte++;
2103 }
2104
2105 //
2106 // Unlock the working set and update quota charges if needed, then return
2107 //
2108 MiUnlockProcessWorkingSetUnsafe(Process, Thread);
2109 if ((QuotaCharge > 0) && (!DontCharge))
2110 {
2111 FoundVad->u.VadFlags.CommitCharge -= QuotaCharge;
2112 Process->CommitCharge -= QuotaCharge;
2113 }
2114 return STATUS_SUCCESS;
2115 }
2116
2117 VOID
2118 NTAPI
2119 MiRemoveMappedPtes(IN PVOID BaseAddress,
2120 IN ULONG NumberOfPtes,
2121 IN PCONTROL_AREA ControlArea,
2122 IN PMMSUPPORT Ws)
2123 {
2124 PMMPTE PointerPte;//, FirstPte;
2125 PMMPDE PointerPde, SystemMapPde;
2126 PMMPFN Pfn1, Pfn2;
2127 MMPTE PteContents;
2128 KIRQL OldIrql;
2129 DPRINT("Removing mapped view at: 0x%p\n", BaseAddress);
2130
2131 /* Get the PTE and loop each one */
2132 PointerPte = MiAddressToPte(BaseAddress);
2133 //FirstPte = PointerPte;
2134 while (NumberOfPtes)
2135 {
2136 /* Check if the PTE is already valid */
2137 PteContents = *PointerPte;
2138 if (PteContents.u.Hard.Valid == 1)
2139 {
2140 /* Get the PFN entry */
2141 Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
2142
2143 /* Get the PTE */
2144 PointerPde = MiAddressToPte(PointerPte);
2145
2146 /* Lock the PFN database and make sure this isn't a mapped file */
2147 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2148 ASSERT(((Pfn1->u3.e1.PrototypePte) && (Pfn1->OriginalPte.u.Soft.Prototype)) == 0);
2149
2150 /* FIXME: Dirty bit management */
2151
2152 /* Was the PDE invalid */
2153 if (PointerPde->u.Long == 0)
2154 {
2155 #if (_MI_PAGING_LEVELS == 2)
2156 /* Find the system double-mapped PDE that describes this mapping */
2157 SystemMapPde = &MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)];
2158
2159 /* Make it valid */
2160 ASSERT(SystemMapPde->u.Hard.Valid == 1);
2161 MI_WRITE_VALID_PDE(PointerPde, *SystemMapPde);
2162 #else
2163 ASSERT(FALSE);
2164 #endif
2165 }
2166
2167 /* Dereference the PDE and the PTE */
2168 Pfn2 = MiGetPfnEntry(PFN_FROM_PTE(PointerPde));
2169 //MiDecrementShareCount(Pfn2, PFN_FROM_PTE(PointerPde));
2170 DBG_UNREFERENCED_LOCAL_VARIABLE(Pfn2);
2171 MiDecrementShareCount(Pfn1, PFN_FROM_PTE(&PteContents));
2172
2173 /* Release the PFN lock */
2174 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2175 }
2176 else
2177 {
2178 /* Windows ASSERT */
2179 ASSERT((PteContents.u.Long == 0) || (PteContents.u.Soft.Prototype == 1));
2180
2181 /* But not handled in ARM3 */
2182 ASSERT(PteContents.u.Soft.Prototype == 0);
2183 }
2184
2185 /* Make the PTE into a zero PTE */
2186 PointerPte->u.Long = 0;
2187
2188 /* Move to the next PTE */
2189 PointerPte++;
2190 NumberOfPtes--;
2191 }
2192
2193 /* Flush the TLB */
2194 KeFlushCurrentTb();
2195
2196 /* Acquire the PFN lock */
2197 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2198
2199 /* Decrement the accounting counters */
2200 ControlArea->NumberOfUserReferences--;
2201 ControlArea->NumberOfMappedViews--;
2202
2203 /* Check if we should destroy the CA and release the lock */
2204 MiCheckControlArea(ControlArea, OldIrql);
2205 }
2206
2207 ULONG
2208 NTAPI
2209 MiRemoveFromSystemSpace(IN PMMSESSION Session,
2210 IN PVOID Base,
2211 OUT PCONTROL_AREA *ControlArea)
2212 {
2213 ULONG Hash, Size, Count = 0;
2214 ULONG_PTR Entry;
2215 PAGED_CODE();
2216
2217 /* Compute the hash for this entry and loop trying to find it */
2218 Entry = (ULONG_PTR)Base >> 16;
2219 Hash = Entry % Session->SystemSpaceHashKey;
2220 while ((Session->SystemSpaceViewTable[Hash].Entry >> 16) != Entry)
2221 {
2222 /* Check if we overflew past the end of the hash table */
2223 if (++Hash >= Session->SystemSpaceHashSize)
2224 {
2225 /* Reset the hash to zero and keep searching from the bottom */
2226 Hash = 0;
2227 if (++Count == 2)
2228 {
2229 /* But if we overflew twice, then this is not a real mapping */
2230 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2231 (ULONG_PTR)Base,
2232 1,
2233 0,
2234 0);
2235 }
2236 }
2237 }
2238
2239 /* One less entry */
2240 Session->SystemSpaceHashEntries--;
2241
2242 /* Extract the size and clear the entry */
2243 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2244 Session->SystemSpaceViewTable[Hash].Entry = 0;
2245
2246 /* Return the control area and the size */
2247 *ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2248 return Size;
2249 }
2250
2251 NTSTATUS
2252 NTAPI
2253 MiUnmapViewInSystemSpace(IN PMMSESSION Session,
2254 IN PVOID MappedBase)
2255 {
2256 ULONG Size;
2257 PCONTROL_AREA ControlArea;
2258 PAGED_CODE();
2259
2260 /* Remove this mapping */
2261 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2262 Size = MiRemoveFromSystemSpace(Session, MappedBase, &ControlArea);
2263
2264 /* Clear the bits for this mapping */
2265 RtlClearBits(Session->SystemSpaceBitMap,
2266 (ULONG)(((ULONG_PTR)MappedBase - (ULONG_PTR)Session->SystemSpaceViewStart) >> 16),
2267 Size);
2268
2269 /* Convert the size from a bit size into the actual size */
2270 Size = Size * (_64K >> PAGE_SHIFT);
2271
2272 /* Remove the PTEs now */
2273 MiRemoveMappedPtes(MappedBase, Size, ControlArea, NULL);
2274 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2275
2276 /* Return success */
2277 return STATUS_SUCCESS;
2278 }
2279
2280 /* PUBLIC FUNCTIONS ***********************************************************/
2281
2282 /*
2283 * @implemented
2284 */
2285 NTSTATUS
2286 NTAPI
2287 MmCreateArm3Section(OUT PVOID *SectionObject,
2288 IN ACCESS_MASK DesiredAccess,
2289 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
2290 IN PLARGE_INTEGER InputMaximumSize,
2291 IN ULONG SectionPageProtection,
2292 IN ULONG AllocationAttributes,
2293 IN HANDLE FileHandle OPTIONAL,
2294 IN PFILE_OBJECT FileObject OPTIONAL)
2295 {
2296 SECTION Section;
2297 PSECTION NewSection;
2298 PSUBSECTION Subsection;
2299 PSEGMENT NewSegment, Segment;
2300 NTSTATUS Status;
2301 PCONTROL_AREA ControlArea;
2302 ULONG ProtectionMask, ControlAreaSize, Size, NonPagedCharge, PagedCharge;
2303 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
2304 BOOLEAN FileLock = FALSE, KernelCall = FALSE;
2305 KIRQL OldIrql;
2306 PFILE_OBJECT File;
2307 PVOID PreviousSectionPointer;
2308
2309 /* Make the same sanity checks that the Nt interface should've validated */
2310 ASSERT((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
2311 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
2312 SEC_NO_CHANGE)) == 0);
2313 ASSERT((AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)) != 0);
2314 ASSERT(!((AllocationAttributes & SEC_IMAGE) &&
2315 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE |
2316 SEC_NOCACHE | SEC_NO_CHANGE))));
2317 ASSERT(!((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE)));
2318 ASSERT(!((SectionPageProtection & PAGE_NOCACHE) ||
2319 (SectionPageProtection & PAGE_WRITECOMBINE) ||
2320 (SectionPageProtection & PAGE_GUARD) ||
2321 (SectionPageProtection & PAGE_NOACCESS)));
2322
2323 /* Convert section flag to page flag */
2324 if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
2325
2326 /* Check to make sure the protection is correct. Nt* does this already */
2327 ProtectionMask = MiMakeProtectionMask(SectionPageProtection);
2328 if (ProtectionMask == MM_INVALID_PROTECTION) return STATUS_INVALID_PAGE_PROTECTION;
2329
2330 /* Check if this is going to be a data or image backed file section */
2331 if ((FileHandle) || (FileObject))
2332 {
2333 /* These cannot be mapped with large pages */
2334 if (AllocationAttributes & SEC_LARGE_PAGES) return STATUS_INVALID_PARAMETER_6;
2335
2336 /* For now, only support the mechanism through a file handle */
2337 ASSERT(FileObject == NULL);
2338
2339 /* Reference the file handle to get the object */
2340 Status = ObReferenceObjectByHandle(FileHandle,
2341 MmMakeFileAccess[ProtectionMask],
2342 IoFileObjectType,
2343 PreviousMode,
2344 (PVOID*)&File,
2345 NULL);
2346 if (!NT_SUCCESS(Status)) return Status;
2347
2348 /* Make sure Cc has been doing its job */
2349 if (!File->SectionObjectPointer)
2350 {
2351 /* This is not a valid file system-based file, fail */
2352 ObDereferenceObject(File);
2353 return STATUS_INVALID_FILE_FOR_SECTION;
2354 }
2355
2356 /* Image-file backed sections are not yet supported */
2357 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2358
2359 /* Compute the size of the control area, and allocate it */
2360 ControlAreaSize = sizeof(CONTROL_AREA) + sizeof(MSUBSECTION);
2361 ControlArea = ExAllocatePoolWithTag(NonPagedPool, ControlAreaSize, 'aCmM');
2362 if (!ControlArea)
2363 {
2364 ObDereferenceObject(File);
2365 return STATUS_INSUFFICIENT_RESOURCES;
2366 }
2367
2368 /* Zero it out */
2369 RtlZeroMemory(ControlArea, ControlAreaSize);
2370
2371 /* Did we get a handle, or an object? */
2372 if (FileHandle)
2373 {
2374 /* We got a file handle so we have to lock down the file */
2375 #if 0
2376 Status = FsRtlAcquireToCreateMappedSection(File, SectionPageProtection);
2377 if (!NT_SUCCESS(Status))
2378 {
2379 ExFreePool(ControlArea);
2380 ObDereferenceObject(File);
2381 return Status;
2382 }
2383 #else
2384 /* ReactOS doesn't support this API yet, so do nothing */
2385 Status = STATUS_SUCCESS;
2386 #endif
2387 /* Update the top-level IRP so that drivers know what's happening */
2388 IoSetTopLevelIrp((PIRP)FSRTL_FSP_TOP_LEVEL_IRP);
2389 FileLock = TRUE;
2390 }
2391
2392 /* Lock the PFN database while we play with the section pointers */
2393 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2394
2395 /* Image-file backed sections are not yet supported */
2396 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2397
2398 /* There should not already be a control area for this file */
2399 ASSERT(File->SectionObjectPointer->DataSectionObject == NULL);
2400 NewSegment = NULL;
2401
2402 /* Write down that this CA is being created, and set it */
2403 ControlArea->u.Flags.BeingCreated = TRUE;
2404 PreviousSectionPointer = File->SectionObjectPointer;
2405 File->SectionObjectPointer->DataSectionObject = ControlArea;
2406
2407 /* We can release the PFN lock now */
2408 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2409
2410 /* We don't support previously-mapped file */
2411 ASSERT(NewSegment == NULL);
2412
2413 /* Image-file backed sections are not yet supported */
2414 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2415
2416 /* So we always create a data file map */
2417 Status = MiCreateDataFileMap(File,
2418 &Segment,
2419 (PSIZE_T)InputMaximumSize,
2420 SectionPageProtection,
2421 AllocationAttributes,
2422 KernelCall);
2423 ASSERT(PreviousSectionPointer == File->SectionObjectPointer);
2424 ASSERT(NT_SUCCESS(Status));
2425
2426 /* Check if a maximum size was specified */
2427 if (!InputMaximumSize->QuadPart)
2428 {
2429 /* Nope, use the segment size */
2430 Section.SizeOfSection.QuadPart = (LONGLONG)Segment->SizeOfSegment;
2431 }
2432 else
2433 {
2434 /* Yep, use the entered size */
2435 Section.SizeOfSection.QuadPart = InputMaximumSize->QuadPart;
2436 }
2437 }
2438 else
2439 {
2440 /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
2441 if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
2442
2443 /* Not yet supported */
2444 ASSERT((AllocationAttributes & SEC_LARGE_PAGES) == 0);
2445
2446 /* So this must be a pagefile-backed section, create the mappings needed */
2447 Status = MiCreatePagingFileMap(&NewSegment,
2448 (PSIZE_T)InputMaximumSize,
2449 ProtectionMask,
2450 AllocationAttributes);
2451 if (!NT_SUCCESS(Status)) return Status;
2452
2453 /* Set the size here, and read the control area */
2454 Section.SizeOfSection.QuadPart = NewSegment->SizeOfSegment;
2455 ControlArea = NewSegment->ControlArea;
2456 }
2457
2458 /* Did we already have a segment? */
2459 if (!NewSegment)
2460 {
2461 /* This must be the file path and we created a segment */
2462 NewSegment = Segment;
2463 ASSERT(File != NULL);
2464
2465 /* Acquire the PFN lock while we set control area flags */
2466 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2467
2468 /* We don't support this race condition yet, so assume no waiters */
2469 ASSERT(ControlArea->WaitingForDeletion == NULL);
2470 ControlArea->WaitingForDeletion = NULL;
2471
2472 /* Image-file backed sections are not yet supported, nor ROM images */
2473 ASSERT((AllocationAttributes & SEC_IMAGE) == 0);
2474 ASSERT(Segment->ControlArea->u.Flags.Rom == 0);
2475
2476 /* Take off the being created flag, and then release the lock */
2477 ControlArea->u.Flags.BeingCreated = FALSE;
2478 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2479 }
2480
2481 /* Check if we locked the file earlier */
2482 if (FileLock)
2483 {
2484 /* Reset the top-level IRP and release the lock */
2485 IoSetTopLevelIrp(NULL);
2486 //FsRtlReleaseFile(File);
2487 FileLock = FALSE;
2488 }
2489
2490 /* Set the initial section object data */
2491 Section.InitialPageProtection = SectionPageProtection;
2492
2493 /* The mapping created a control area and segment, save the flags */
2494 Section.Segment = NewSegment;
2495 Section.u.LongFlags = ControlArea->u.LongFlags;
2496
2497 /* Check if this is a user-mode read-write non-image file mapping */
2498 if (!(FileObject) &&
2499 (SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)) &&
2500 (ControlArea->u.Flags.Image == 0) &&
2501 (ControlArea->FilePointer != NULL))
2502 {
2503 /* Add a reference and set the flag */
2504 Section.u.Flags.UserWritable = 1;
2505 InterlockedIncrement((PLONG)&ControlArea->WritableUserReferences);
2506 }
2507
2508 /* Check for image mappings or page file mappings */
2509 if ((ControlArea->u.Flags.Image == 1) || !(ControlArea->FilePointer))
2510 {
2511 /* Charge the segment size, and allocate a subsection */
2512 PagedCharge = sizeof(SECTION) + NewSegment->TotalNumberOfPtes * sizeof(MMPTE);
2513 Size = sizeof(SUBSECTION);
2514 }
2515 else
2516 {
2517 /* Charge nothing, and allocate a mapped subsection */
2518 PagedCharge = 0;
2519 Size = sizeof(MSUBSECTION);
2520 }
2521
2522 /* Check if this is a normal CA */
2523 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2524 ASSERT(ControlArea->u.Flags.Rom == 0);
2525
2526 /* Charge only a CA, and the subsection is right after */
2527 NonPagedCharge = sizeof(CONTROL_AREA);
2528 Subsection = (PSUBSECTION)(ControlArea + 1);
2529
2530 /* We only support single-subsection mappings */
2531 NonPagedCharge += Size;
2532 ASSERT(Subsection->NextSubsection == NULL);
2533
2534 /* Create the actual section object, with enough space for the prototype PTEs */
2535 Status = ObCreateObject(PreviousMode,
2536 MmSectionObjectType,
2537 ObjectAttributes,
2538 PreviousMode,
2539 NULL,
2540 sizeof(SECTION),
2541 PagedCharge,
2542 NonPagedCharge,
2543 (PVOID*)&NewSection);
2544 ASSERT(NT_SUCCESS(Status));
2545
2546 /* Now copy the local section object from the stack into this new object */
2547 RtlCopyMemory(NewSection, &Section, sizeof(SECTION));
2548 NewSection->Address.StartingVpn = 0;
2549
2550 /* For now, only user calls are supported */
2551 ASSERT(KernelCall == FALSE);
2552 NewSection->u.Flags.UserReference = TRUE;
2553
2554 /* Migrate the attribute into a flag */
2555 if (AllocationAttributes & SEC_NO_CHANGE) NewSection->u.Flags.NoChange = TRUE;
2556
2557 /* If R/W access is not requested, this might eventually become a CoW mapping */
2558 if (!(SectionPageProtection & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE)))
2559 {
2560 NewSection->u.Flags.CopyOnWrite = TRUE;
2561 }
2562
2563 /* Is this a "based" allocation, in which all mappings are identical? */
2564 if (AllocationAttributes & SEC_BASED)
2565 {
2566 /* Convert the flag, and make sure the section isn't too big */
2567 NewSection->u.Flags.Based = TRUE;
2568 if ((ULONGLONG)NewSection->SizeOfSection.QuadPart >
2569 (ULONG_PTR)MmHighSectionBase)
2570 {
2571 DPRINT1("BASED section is too large\n");
2572 ObDereferenceObject(NewSection);
2573 return STATUS_NO_MEMORY;
2574 }
2575
2576 /* Lock the VAD tree during the search */
2577 KeAcquireGuardedMutex(&MmSectionBasedMutex);
2578
2579 /* Find an address top-down */
2580 Status = MiFindEmptyAddressRangeDownBasedTree(NewSection->SizeOfSection.LowPart,
2581 (ULONG_PTR)MmHighSectionBase,
2582 _64K,
2583 &MmSectionBasedRoot,
2584 &NewSection->Address.StartingVpn);
2585 ASSERT(NT_SUCCESS(Status));
2586
2587 /* Compute the ending address and insert it into the VAD tree */
2588 NewSection->Address.EndingVpn = NewSection->Address.StartingVpn +
2589 NewSection->SizeOfSection.LowPart -
2590 1;
2591 MiInsertBasedSection(NewSection);
2592
2593 /* Finally release the lock */
2594 KeReleaseGuardedMutex(&MmSectionBasedMutex);
2595 }
2596
2597 /* Write down if this was a kernel call */
2598 ControlArea->u.Flags.WasPurged |= KernelCall;
2599 ASSERT(ControlArea->u.Flags.WasPurged == FALSE);
2600
2601 /* Make sure the segment and the section are the same size, or the section is smaller */
2602 ASSERT((ULONG64)NewSection->SizeOfSection.QuadPart <= NewSection->Segment->SizeOfSegment);
2603
2604 /* Return the object and the creation status */
2605 *SectionObject = (PVOID)NewSection;
2606 return Status;
2607 }
2608
2609 /*
2610 * @implemented
2611 */
2612 NTSTATUS
2613 NTAPI
2614 MmMapViewOfArm3Section(IN PVOID SectionObject,
2615 IN PEPROCESS Process,
2616 IN OUT PVOID *BaseAddress,
2617 IN ULONG_PTR ZeroBits,
2618 IN SIZE_T CommitSize,
2619 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
2620 IN OUT PSIZE_T ViewSize,
2621 IN SECTION_INHERIT InheritDisposition,
2622 IN ULONG AllocationType,
2623 IN ULONG Protect)
2624 {
2625 KAPC_STATE ApcState;
2626 BOOLEAN Attached = FALSE;
2627 PSECTION Section;
2628 PCONTROL_AREA ControlArea;
2629 ULONG ProtectionMask;
2630 NTSTATUS Status;
2631 PAGED_CODE();
2632
2633 /* Get the segment and control area */
2634 Section = (PSECTION)SectionObject;
2635 ControlArea = Section->Segment->ControlArea;
2636
2637 /* These flags/states are not yet supported by ARM3 */
2638 ASSERT(Section->u.Flags.Image == 0);
2639 ASSERT(Section->u.Flags.NoCache == 0);
2640 ASSERT(Section->u.Flags.WriteCombined == 0);
2641 ASSERT((AllocationType & MEM_RESERVE) == 0);
2642 ASSERT(ControlArea->u.Flags.PhysicalMemory == 0);
2643
2644 /* Check if the mapping protection is compatible with the create */
2645 if (!MiIsProtectionCompatible(Section->InitialPageProtection, Protect))
2646 {
2647 DPRINT1("Mapping protection is incompatible\n");
2648 return STATUS_SECTION_PROTECTION;
2649 }
2650
2651 /* Check if the offset and size would cause an overflow */
2652 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) <
2653 (ULONG64)SectionOffset->QuadPart)
2654 {
2655 DPRINT1("Section offset overflows\n");
2656 return STATUS_INVALID_VIEW_SIZE;
2657 }
2658
2659 /* Check if the offset and size are bigger than the section itself */
2660 if (((ULONG64)SectionOffset->QuadPart + *ViewSize) >
2661 (ULONG64)Section->SizeOfSection.QuadPart)
2662 {
2663 DPRINT1("Section offset is larger than section\n");
2664 return STATUS_INVALID_VIEW_SIZE;
2665 }
2666
2667 /* Check if the caller did not specify a view size */
2668 if (!(*ViewSize))
2669 {
2670 /* Compute it for the caller */
2671 *ViewSize = (SIZE_T)(Section->SizeOfSection.QuadPart - SectionOffset->QuadPart);
2672
2673 /* Check if it's larger than 4GB or overflows into kernel-mode */
2674 if ((*ViewSize > 0xFFFFFFFF) ||
2675 (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)*BaseAddress) < *ViewSize))
2676 {
2677 DPRINT1("Section view won't fit\n");
2678 return STATUS_INVALID_VIEW_SIZE;
2679 }
2680 }
2681
2682 /* Check if the commit size is larger than the view size */
2683 if (CommitSize > *ViewSize)
2684 {
2685 DPRINT1("Attempting to commit more than the view itself\n");
2686 return STATUS_INVALID_PARAMETER_5;
2687 }
2688
2689 /* Check if the view size is larger than the section */
2690 if (*ViewSize > (ULONG64)Section->SizeOfSection.QuadPart)
2691 {
2692 DPRINT1("The view is larger than the section\n");
2693 return STATUS_INVALID_VIEW_SIZE;
2694 }
2695
2696 /* Compute and validate the protection mask */
2697 ProtectionMask = MiMakeProtectionMask(Protect);
2698 if (ProtectionMask == MM_INVALID_PROTECTION)
2699 {
2700 DPRINT1("The protection is invalid\n");
2701 return STATUS_INVALID_PAGE_PROTECTION;
2702 }
2703
2704 /* We only handle pagefile-backed sections, which cannot be writecombined */
2705 if (Protect & PAGE_WRITECOMBINE)
2706 {
2707 DPRINT1("Cannot write combine a pagefile-backed section\n");
2708 return STATUS_INVALID_PARAMETER_10;
2709 }
2710
2711 /* Start by attaching to the current process if needed */
2712 if (PsGetCurrentProcess() != Process)
2713 {
2714 KeStackAttachProcess(&Process->Pcb, &ApcState);
2715 Attached = TRUE;
2716 }
2717
2718 /* Lock the address space and make sure the process is alive */
2719 MmLockAddressSpace(&Process->Vm);
2720 if (!Process->VmDeleted)
2721 {
2722 /* Do the actual mapping */
2723 DPRINT("Mapping ARM3 data section\n");
2724 Status = MiMapViewOfDataSection(ControlArea,
2725 Process,
2726 BaseAddress,
2727 SectionOffset,
2728 ViewSize,
2729 Section,
2730 InheritDisposition,
2731 ProtectionMask,
2732 CommitSize,
2733 ZeroBits,
2734 AllocationType);
2735 }
2736 else
2737 {
2738 /* The process is being terminated, fail */
2739 DPRINT1("The process is dying\n");
2740 Status = STATUS_PROCESS_IS_TERMINATING;
2741 }
2742
2743 /* Unlock the address space and detatch if needed, then return status */
2744 MmUnlockAddressSpace(&Process->Vm);
2745 if (Attached) KeUnstackDetachProcess(&ApcState);
2746 return Status;
2747 }
2748
2749 /*
2750 * @unimplemented
2751 */
2752 BOOLEAN
2753 NTAPI
2754 MmDisableModifiedWriteOfSection(IN PSECTION_OBJECT_POINTERS SectionObjectPointer)
2755 {
2756 UNIMPLEMENTED;
2757 return FALSE;
2758 }
2759
2760 /*
2761 * @unimplemented
2762 */
2763 BOOLEAN
2764 NTAPI
2765 MmForceSectionClosed(IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
2766 IN BOOLEAN DelayClose)
2767 {
2768 UNIMPLEMENTED;
2769 return FALSE;
2770 }
2771
2772 /*
2773 * @implemented
2774 */
2775 NTSTATUS
2776 NTAPI
2777 MmMapViewInSessionSpace(IN PVOID Section,
2778 OUT PVOID *MappedBase,
2779 IN OUT PSIZE_T ViewSize)
2780 {
2781 PAGED_CODE();
2782
2783 // HACK
2784 if (MiIsRosSectionObject(Section))
2785 {
2786 return MmMapViewInSystemSpace(Section, MappedBase, ViewSize);
2787 }
2788
2789 /* Process must be in a session */
2790 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2791 {
2792 DPRINT1("Process is not in session\n");
2793 return STATUS_NOT_MAPPED_VIEW;
2794 }
2795
2796 /* Use the system space API, but with the session view instead */
2797 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2798 return MiMapViewInSystemSpace(Section,
2799 &MmSessionSpace->Session,
2800 MappedBase,
2801 ViewSize);
2802 }
2803
2804 /*
2805 * @implemented
2806 */
2807 NTSTATUS
2808 NTAPI
2809 MmUnmapViewInSessionSpace(IN PVOID MappedBase)
2810 {
2811 PAGED_CODE();
2812
2813 // HACK
2814 if (!MI_IS_SESSION_ADDRESS(MappedBase))
2815 {
2816 return MmUnmapViewInSystemSpace(MappedBase);
2817 }
2818
2819 /* Process must be in a session */
2820 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2821 {
2822 DPRINT1("Proess is not in session\n");
2823 return STATUS_NOT_MAPPED_VIEW;
2824 }
2825
2826 /* Use the system space API, but with the session view instead */
2827 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2828 return MiUnmapViewInSystemSpace(&MmSessionSpace->Session,
2829 MappedBase);
2830 }
2831
2832 /*
2833 * @implemented
2834 */
2835 NTSTATUS
2836 NTAPI
2837 MmUnmapViewOfSection(IN PEPROCESS Process,
2838 IN PVOID BaseAddress)
2839 {
2840 return MiUnmapViewOfSection(Process, BaseAddress, 0);
2841 }
2842
2843 /*
2844 * @implemented
2845 */
2846 NTSTATUS
2847 NTAPI
2848 MmUnmapViewInSystemSpace(IN PVOID MappedBase)
2849 {
2850 PMEMORY_AREA MemoryArea;
2851 PAGED_CODE();
2852
2853 /* Was this mapped by RosMm? */
2854 MemoryArea = MmLocateMemoryAreaByAddress(MmGetKernelAddressSpace(), MappedBase);
2855 if ((MemoryArea) && (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3))
2856 {
2857 return MiRosUnmapViewInSystemSpace(MappedBase);
2858 }
2859
2860 /* It was not, call the ARM3 routine */
2861 return MiUnmapViewInSystemSpace(&MmSession, MappedBase);
2862 }
2863
2864 /*
2865 * @implemented
2866 */
2867 NTSTATUS
2868 NTAPI
2869 MmCommitSessionMappedView(IN PVOID MappedBase,
2870 IN SIZE_T ViewSize)
2871 {
2872 ULONG_PTR StartAddress, EndingAddress, Base;
2873 ULONG Hash, Count = 0, Size, QuotaCharge;
2874 PMMSESSION Session;
2875 PMMPTE LastProtoPte, PointerPte, ProtoPte;
2876 PCONTROL_AREA ControlArea;
2877 PSEGMENT Segment;
2878 PSUBSECTION Subsection;
2879 MMPTE TempPte;
2880 PAGED_CODE();
2881
2882 /* Make sure the base isn't past the session view range */
2883 if ((MappedBase < MiSessionViewStart) ||
2884 (MappedBase >= (PVOID)((ULONG_PTR)MiSessionViewStart + MmSessionViewSize)))
2885 {
2886 DPRINT1("Base outside of valid range\n");
2887 return STATUS_INVALID_PARAMETER_1;
2888 }
2889
2890 /* Make sure the size isn't past the session view range */
2891 if (((ULONG_PTR)MiSessionViewStart + MmSessionViewSize -
2892 (ULONG_PTR)MappedBase) < ViewSize)
2893 {
2894 DPRINT1("Size outside of valid range\n");
2895 return STATUS_INVALID_PARAMETER_2;
2896 }
2897
2898 /* Sanity check */
2899 ASSERT(ViewSize != 0);
2900
2901 /* Process must be in a session */
2902 if (PsGetCurrentProcess()->ProcessInSession == FALSE)
2903 {
2904 DPRINT1("Process is not in session\n");
2905 return STATUS_NOT_MAPPED_VIEW;
2906 }
2907
2908 /* Compute the correctly aligned base and end addresses */
2909 StartAddress = (ULONG_PTR)PAGE_ALIGN(MappedBase);
2910 EndingAddress = ((ULONG_PTR)MappedBase + ViewSize - 1) | (PAGE_SIZE - 1);
2911
2912 /* Sanity check and grab the session */
2913 ASSERT(MmIsAddressValid(MmSessionSpace) == TRUE);
2914 Session = &MmSessionSpace->Session;
2915
2916 /* Get the hash entry for this allocation */
2917 Hash = (StartAddress >> 16) % Session->SystemSpaceHashKey;
2918
2919 /* Lock system space */
2920 KeAcquireGuardedMutex(Session->SystemSpaceViewLockPointer);
2921
2922 /* Loop twice so we can try rolling over if needed */
2923 while (TRUE)
2924 {
2925 /* Extract the size and base addresses from the entry */
2926 Base = Session->SystemSpaceViewTable[Hash].Entry & ~0xFFFF;
2927 Size = Session->SystemSpaceViewTable[Hash].Entry & 0xFFFF;
2928
2929 /* Convert the size to bucket chunks */
2930 Size *= MI_SYSTEM_VIEW_BUCKET_SIZE;
2931
2932 /* Bail out if this entry fits in here */
2933 if ((StartAddress >= Base) && (EndingAddress < (Base + Size))) break;
2934
2935 /* Check if we overflew past the end of the hash table */
2936 if (++Hash >= Session->SystemSpaceHashSize)
2937 {
2938 /* Reset the hash to zero and keep searching from the bottom */
2939 Hash = 0;
2940 if (++Count == 2)
2941 {
2942 /* But if we overflew twice, then this is not a real mapping */
2943 KeBugCheckEx(DRIVER_UNMAPPING_INVALID_VIEW,
2944 Base,
2945 2,
2946 0,
2947 0);
2948 }
2949 }
2950 }
2951
2952 /* Make sure the view being mapped is not file-based */
2953 ControlArea = Session->SystemSpaceViewTable[Hash].ControlArea;
2954 if (ControlArea->FilePointer != NULL)
2955 {
2956 /* It is, so we have to bail out */
2957 DPRINT1("Only page-filed backed sections can be commited\n");
2958 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2959 return STATUS_ALREADY_COMMITTED;
2960 }
2961
2962 /* Get the subsection. We don't support LARGE_CONTROL_AREA in ARM3 */
2963 ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0);
2964 ASSERT(ControlArea->u.Flags.Rom == 0);
2965 Subsection = (PSUBSECTION)(ControlArea + 1);
2966
2967 /* Get the start and end PTEs -- make sure the end PTE isn't past the end */
2968 ProtoPte = Subsection->SubsectionBase + ((StartAddress - Base) >> PAGE_SHIFT);
2969 QuotaCharge = MiAddressToPte(EndingAddress) - MiAddressToPte(StartAddress) + 1;
2970 LastProtoPte = ProtoPte + QuotaCharge;
2971 if (LastProtoPte >= Subsection->SubsectionBase + Subsection->PtesInSubsection)
2972 {
2973 DPRINT1("PTE is out of bounds\n");
2974 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2975 return STATUS_INVALID_PARAMETER_2;
2976 }
2977
2978 /* Acquire the commit lock and count all the non-committed PTEs */
2979 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex);
2980 PointerPte = ProtoPte;
2981 while (PointerPte < LastProtoPte)
2982 {
2983 if (PointerPte->u.Long) QuotaCharge--;
2984 PointerPte++;
2985 }
2986
2987 /* Was everything committed already? */
2988 if (!QuotaCharge)
2989 {
2990 /* Nothing to do! */
2991 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
2992 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
2993 return STATUS_SUCCESS;
2994 }
2995
2996 /* Pick the segment and template PTE */
2997 Segment = ControlArea->Segment;
2998 TempPte = Segment->SegmentPteTemplate;
2999 ASSERT(TempPte.u.Long != 0);
3000
3001 /* Loop all prototype PTEs to be committed */
3002 PointerPte = ProtoPte;
3003 while (PointerPte < LastProtoPte)
3004 {
3005 /* Make sure the PTE is already invalid */
3006 if (PointerPte->u.Long == 0)
3007 {
3008 /* And write the invalid PTE */
3009 MI_WRITE_INVALID_PTE(PointerPte, TempPte);
3010 }
3011
3012 /* Move to the next PTE */
3013 PointerPte++;
3014 }
3015
3016 /* Check if we had at least one page charged */
3017 if (QuotaCharge)
3018 {
3019 /* Update the accounting data */
3020 Segment->NumberOfCommittedPages += QuotaCharge;
3021 InterlockedExchangeAddSizeT(&MmSharedCommit, QuotaCharge);
3022 }
3023
3024 /* Release all */
3025 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex);
3026 KeReleaseGuardedMutex(Session->SystemSpaceViewLockPointer);
3027 return STATUS_SUCCESS;
3028 }
3029
3030 /* SYSTEM CALLS ***************************************************************/
3031
3032 NTSTATUS
3033 NTAPI
3034 NtAreMappedFilesTheSame(IN PVOID File1MappedAsAnImage,
3035 IN PVOID File2MappedAsFile)
3036 {
3037 PVOID AddressSpace;
3038 PMEMORY_AREA MemoryArea1, MemoryArea2;
3039 PROS_SECTION_OBJECT Section1, Section2;
3040
3041 /* Lock address space */
3042 AddressSpace = MmGetCurrentAddressSpace();
3043 MmLockAddressSpace(AddressSpace);
3044
3045 /* Locate the memory area for the process by address */
3046 MemoryArea1 = MmLocateMemoryAreaByAddress(AddressSpace, File1MappedAsAnImage);
3047 if (!MemoryArea1)
3048 {
3049 /* Fail, the address does not exist */
3050 MmUnlockAddressSpace(AddressSpace);
3051 return STATUS_INVALID_ADDRESS;
3052 }
3053
3054 /* Check if it's a section view (RosMm section) or ARM3 section */
3055 if (MemoryArea1->Type != MEMORY_AREA_SECTION_VIEW)
3056 {
3057 /* Fail, the address is not a section */
3058 MmUnlockAddressSpace(AddressSpace);
3059 return STATUS_CONFLICTING_ADDRESSES;
3060 }
3061
3062 /* Get the section pointer to the SECTION_OBJECT */
3063 Section1 = MemoryArea1->Data.SectionData.Section;
3064 if (Section1->FileObject == NULL)
3065 {
3066 MmUnlockAddressSpace(AddressSpace);
3067 return STATUS_CONFLICTING_ADDRESSES;
3068 }
3069
3070 /* Locate the memory area for the process by address */
3071 MemoryArea2 = MmLocateMemoryAreaByAddress(AddressSpace, File2MappedAsFile);
3072 if (!MemoryArea2)
3073 {
3074 /* Fail, the address does not exist */
3075 MmUnlockAddressSpace(AddressSpace);
3076 return STATUS_INVALID_ADDRESS;
3077 }
3078
3079 /* Check if it's a section view (RosMm section) or ARM3 section */
3080 if (MemoryArea2->Type != MEMORY_AREA_SECTION_VIEW)
3081 {
3082 /* Fail, the address is not a section */
3083 MmUnlockAddressSpace(AddressSpace);
3084 return STATUS_CONFLICTING_ADDRESSES;
3085 }
3086
3087 /* Get the section pointer to the SECTION_OBJECT */
3088 Section2 = MemoryArea2->Data.SectionData.Section;
3089 if (Section2->FileObject == NULL)
3090 {
3091 MmUnlockAddressSpace(AddressSpace);
3092 return STATUS_CONFLICTING_ADDRESSES;
3093 }
3094
3095 /* The shared cache map seems to be the same if both of these are equal */
3096 if (Section1->FileObject->SectionObjectPointer->SharedCacheMap ==
3097 Section2->FileObject->SectionObjectPointer->SharedCacheMap)
3098 {
3099 MmUnlockAddressSpace(AddressSpace);
3100 return STATUS_SUCCESS;
3101 }
3102
3103 /* Unlock address space */
3104 MmUnlockAddressSpace(AddressSpace);
3105 return STATUS_NOT_SAME_DEVICE;
3106 }
3107
3108 /*
3109 * @implemented
3110 */
3111 NTSTATUS
3112 NTAPI
3113 NtCreateSection(OUT PHANDLE SectionHandle,
3114 IN ACCESS_MASK DesiredAccess,
3115 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3116 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3117 IN ULONG SectionPageProtection OPTIONAL,
3118 IN ULONG AllocationAttributes,
3119 IN HANDLE FileHandle OPTIONAL)
3120 {
3121 LARGE_INTEGER SafeMaximumSize;
3122 PVOID SectionObject;
3123 HANDLE Handle;
3124 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3125 NTSTATUS Status;
3126 PAGED_CODE();
3127
3128 /* Check for non-existing flags */
3129 if ((AllocationAttributes & ~(SEC_COMMIT | SEC_RESERVE | SEC_BASED |
3130 SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE |
3131 SEC_NO_CHANGE)))
3132 {
3133 if (!(AllocationAttributes & 1))
3134 {
3135 DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes);
3136 return STATUS_INVALID_PARAMETER_6;
3137 }
3138 }
3139
3140 /* Check for no allocation type */
3141 if (!(AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_IMAGE)))
3142 {
3143 DPRINT1("Missing allocation type in allocation attributes\n");
3144 return STATUS_INVALID_PARAMETER_6;
3145 }
3146
3147 /* Check for image allocation with invalid attributes */
3148 if ((AllocationAttributes & SEC_IMAGE) &&
3149 (AllocationAttributes & (SEC_COMMIT | SEC_RESERVE | SEC_LARGE_PAGES |
3150 SEC_NOCACHE | SEC_NO_CHANGE)))
3151 {
3152 DPRINT1("Image allocation with invalid attributes\n");
3153 return STATUS_INVALID_PARAMETER_6;
3154 }
3155
3156 /* Check for allocation type is both commit and reserve */
3157 if ((AllocationAttributes & SEC_COMMIT) && (AllocationAttributes & SEC_RESERVE))
3158 {
3159 DPRINT1("Commit and reserve in the same time\n");
3160 return STATUS_INVALID_PARAMETER_6;
3161 }
3162
3163 /* Now check for valid protection */
3164 if ((SectionPageProtection & PAGE_NOCACHE) ||
3165 (SectionPageProtection & PAGE_WRITECOMBINE) ||
3166 (SectionPageProtection & PAGE_GUARD) ||
3167 (SectionPageProtection & PAGE_NOACCESS))
3168 {
3169 DPRINT1("Sections don't support these protections\n");
3170 return STATUS_INVALID_PAGE_PROTECTION;
3171 }
3172
3173 /* Use a maximum size of zero, if none was specified */
3174 SafeMaximumSize.QuadPart = 0;
3175
3176 /* Check for user-mode caller */
3177 if (PreviousMode != KernelMode)
3178 {
3179 /* Enter SEH */
3180 _SEH2_TRY
3181 {
3182 /* Safely check user-mode parameters */
3183 if (MaximumSize) SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3184 MaximumSize = &SafeMaximumSize;
3185 ProbeForWriteHandle(SectionHandle);
3186 }
3187 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3188 {
3189 /* Return the exception code */
3190 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3191 }
3192 _SEH2_END;
3193 }
3194 else if (!MaximumSize) MaximumSize = &SafeMaximumSize;
3195
3196 /* Check that MaximumSize is valid if backed by paging file */
3197 if ((!FileHandle) && (!MaximumSize->QuadPart))
3198 return STATUS_INVALID_PARAMETER_4;
3199
3200 /* Create the section */
3201 Status = MmCreateSection(&SectionObject,
3202 DesiredAccess,
3203 ObjectAttributes,
3204 MaximumSize,
3205 SectionPageProtection,
3206 AllocationAttributes,
3207 FileHandle,
3208 NULL);
3209 if (!NT_SUCCESS(Status)) return Status;
3210
3211 /* FIXME: Should zero last page for a file mapping */
3212
3213 /* Now insert the object */
3214 Status = ObInsertObject(SectionObject,
3215 NULL,
3216 DesiredAccess,
3217 0,
3218 NULL,
3219 &Handle);
3220 if (NT_SUCCESS(Status))
3221 {
3222 /* Enter SEH */
3223 _SEH2_TRY
3224 {
3225 /* Return the handle safely */
3226 *SectionHandle = Handle;
3227 }
3228 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3229 {
3230 /* Nothing here */
3231 }
3232 _SEH2_END;
3233 }
3234
3235 /* Return the status */
3236 return Status;
3237 }
3238
3239 NTSTATUS
3240 NTAPI
3241 NtOpenSection(OUT PHANDLE SectionHandle,
3242 IN ACCESS_MASK DesiredAccess,
3243 IN POBJECT_ATTRIBUTES ObjectAttributes)
3244 {
3245 HANDLE Handle;
3246 NTSTATUS Status;
3247 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3248 PAGED_CODE();
3249
3250 /* Check for user-mode caller */
3251 if (PreviousMode != KernelMode)
3252 {
3253 /* Enter SEH */
3254 _SEH2_TRY
3255 {
3256 /* Safely check user-mode parameters */
3257 ProbeForWriteHandle(SectionHandle);
3258 }
3259 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3260 {
3261 /* Return the exception code */
3262 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3263 }
3264 _SEH2_END;
3265 }
3266
3267 /* Try opening the object */
3268 Status = ObOpenObjectByName(ObjectAttributes,
3269 MmSectionObjectType,
3270 PreviousMode,
3271 NULL,
3272 DesiredAccess,
3273 NULL,
3274 &Handle);
3275
3276 /* Enter SEH */
3277 _SEH2_TRY
3278 {
3279 /* Return the handle safely */
3280 *SectionHandle = Handle;
3281 }
3282 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3283 {
3284 /* Nothing here */
3285 }
3286 _SEH2_END;
3287
3288 /* Return the status */
3289 return Status;
3290 }
3291
3292 NTSTATUS
3293 NTAPI
3294 NtMapViewOfSection(IN HANDLE SectionHandle,
3295 IN HANDLE ProcessHandle,
3296 IN OUT PVOID* BaseAddress,
3297 IN ULONG_PTR ZeroBits,
3298 IN SIZE_T CommitSize,
3299 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3300 IN OUT PSIZE_T ViewSize,
3301 IN SECTION_INHERIT InheritDisposition,
3302 IN ULONG AllocationType,
3303 IN ULONG Protect)
3304 {
3305 PVOID SafeBaseAddress;
3306 LARGE_INTEGER SafeSectionOffset;
3307 SIZE_T SafeViewSize;
3308 PROS_SECTION_OBJECT Section;
3309 PEPROCESS Process;
3310 NTSTATUS Status;
3311 ACCESS_MASK DesiredAccess;
3312 ULONG ProtectionMask;
3313 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3314
3315 /* Check for invalid zero bits */
3316 if (ZeroBits > 21) // per-arch?
3317 {
3318 DPRINT1("Invalid zero bits\n");
3319 return STATUS_INVALID_PARAMETER_4;
3320 }
3321
3322 /* Check for invalid inherit disposition */
3323 if ((InheritDisposition > ViewUnmap) || (InheritDisposition < ViewShare))
3324 {
3325 DPRINT1("Invalid inherit disposition\n");
3326 return STATUS_INVALID_PARAMETER_8;
3327 }
3328
3329 /* Allow only valid allocation types */
3330 if ((AllocationType & ~(MEM_TOP_DOWN | MEM_LARGE_PAGES | MEM_DOS_LIM |
3331 SEC_NO_CHANGE | MEM_RESERVE)))
3332 {
3333 DPRINT1("Invalid allocation type\n");
3334 return STATUS_INVALID_PARAMETER_9;
3335 }
3336
3337 /* Convert the protection mask, and validate it */
3338 ProtectionMask = MiMakeProtectionMask(Protect);
3339 if (ProtectionMask == MM_INVALID_PROTECTION)
3340 {
3341 DPRINT1("Invalid page protection\n");
3342 return STATUS_INVALID_PAGE_PROTECTION;
3343 }
3344
3345 /* Check for non-allocation-granularity-aligned BaseAddress */
3346 if (BaseAddress && (*BaseAddress != ALIGN_DOWN_POINTER_BY(*BaseAddress, MM_VIRTMEM_GRANULARITY)))
3347 {
3348 DPRINT("BaseAddress is not at 64-kilobyte address boundary.");
3349 return STATUS_MAPPED_ALIGNMENT;
3350 }
3351
3352 /* Now convert the protection mask into desired section access mask */
3353 DesiredAccess = MmMakeSectionAccess[ProtectionMask & 0x7];
3354
3355 /* Assume no section offset */
3356 SafeSectionOffset.QuadPart = 0;
3357
3358 /* Enter SEH */
3359 _SEH2_TRY
3360 {
3361 /* Check for unsafe parameters */
3362 if (PreviousMode != KernelMode)
3363 {
3364 /* Probe the parameters */
3365 ProbeForWritePointer(BaseAddress);
3366 ProbeForWriteSize_t(ViewSize);
3367 }
3368
3369 /* Check if a section offset was given */
3370 if (SectionOffset)
3371 {
3372 /* Check for unsafe parameters and capture section offset */
3373 if (PreviousMode != KernelMode) ProbeForWriteLargeInteger(SectionOffset);
3374 SafeSectionOffset = *SectionOffset;
3375 }
3376
3377 /* Capture the other parameters */
3378 SafeBaseAddress = *BaseAddress;
3379 SafeViewSize = *ViewSize;
3380 }
3381 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3382 {
3383 /* Return the exception code */
3384 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3385 }
3386 _SEH2_END;
3387
3388 /* Check for kernel-mode address */
3389 if (SafeBaseAddress > MM_HIGHEST_VAD_ADDRESS)
3390 {
3391 DPRINT1("Kernel base not allowed\n");
3392 return STATUS_INVALID_PARAMETER_3;
3393 }
3394
3395 /* Check for range entering kernel-mode */
3396 if (((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - (ULONG_PTR)SafeBaseAddress) < SafeViewSize)
3397 {
3398 DPRINT1("Overflowing into kernel base not allowed\n");
3399 return STATUS_INVALID_PARAMETER_3;
3400 }
3401
3402 /* Check for invalid zero bits */
3403 if (((ULONG_PTR)SafeBaseAddress + SafeViewSize) > (0xFFFFFFFF >> ZeroBits)) // arch?
3404 {
3405 DPRINT1("Invalid zero bits\n");
3406 return STATUS_INVALID_PARAMETER_4;
3407 }
3408
3409 /* Reference the process */
3410 Status = ObReferenceObjectByHandle(ProcessHandle,
3411 PROCESS_VM_OPERATION,
3412 PsProcessType,
3413 PreviousMode,
3414 (PVOID*)&Process,
3415 NULL);
3416 if (!NT_SUCCESS(Status)) return Status;
3417
3418 /* Reference the section */
3419 Status = ObReferenceObjectByHandle(SectionHandle,
3420 DesiredAccess,
3421 MmSectionObjectType,
3422 PreviousMode,
3423 (PVOID*)&Section,
3424 NULL);
3425 if (!NT_SUCCESS(Status))
3426 {
3427 ObDereferenceObject(Process);
3428 return Status;
3429 }
3430
3431 /* Now do the actual mapping */
3432 Status = MmMapViewOfSection(Section,
3433 Process,
3434 &SafeBaseAddress,
3435 ZeroBits,
3436 CommitSize,
3437 &SafeSectionOffset,
3438 &SafeViewSize,
3439 InheritDisposition,
3440 AllocationType,
3441 Protect);
3442
3443 /* Return data only on success */
3444 if (NT_SUCCESS(Status))
3445 {
3446 /* Check if this is an image for the current process */
3447 if ((Section->AllocationAttributes & SEC_IMAGE) &&
3448 (Process == PsGetCurrentProcess()) &&
3449 (Status != STATUS_IMAGE_NOT_AT_BASE))
3450 {
3451 /* Notify the debugger */
3452 DbgkMapViewOfSection(Section,
3453 SafeBaseAddress,
3454 SafeSectionOffset.LowPart,
3455 SafeViewSize);
3456 }
3457
3458 /* Enter SEH */
3459 _SEH2_TRY
3460 {
3461 /* Return parameters to user */
3462 *BaseAddress = SafeBaseAddress;
3463 *ViewSize = SafeViewSize;
3464 if (SectionOffset) *SectionOffset = SafeSectionOffset;
3465 }
3466 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3467 {
3468 /* Nothing to do */
3469 }
3470 _SEH2_END;
3471 }
3472
3473 /* Dereference all objects and return status */
3474 ObDereferenceObject(Section);
3475 ObDereferenceObject(Process);
3476 return Status;
3477 }
3478
3479 NTSTATUS
3480 NTAPI
3481 NtUnmapViewOfSection(IN HANDLE ProcessHandle,
3482 IN PVOID BaseAddress)
3483 {
3484 PEPROCESS Process;
3485 NTSTATUS Status;
3486 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3487
3488 /* Don't allowing mapping kernel views */
3489 if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS))
3490 {
3491 DPRINT1("Trying to unmap a kernel view\n");
3492 return STATUS_NOT_MAPPED_VIEW;
3493 }
3494
3495 /* Reference the process */
3496 Status = ObReferenceObjectByHandle(ProcessHandle,
3497 PROCESS_VM_OPERATION,
3498 PsProcessType,
3499 PreviousMode,
3500 (PVOID*)&Process,
3501 NULL);
3502 if (!NT_SUCCESS(Status)) return Status;
3503
3504 /* Unmap the view */
3505 Status = MiUnmapViewOfSection(Process, BaseAddress, 0);
3506
3507 /* Dereference the process and return status */
3508 ObDereferenceObject(Process);
3509 return Status;
3510 }
3511
3512 NTSTATUS
3513 NTAPI
3514 NtExtendSection(IN HANDLE SectionHandle,
3515 IN OUT PLARGE_INTEGER NewMaximumSize)
3516 {
3517 LARGE_INTEGER SafeNewMaximumSize;
3518 PROS_SECTION_OBJECT Section;
3519 NTSTATUS Status;
3520 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
3521
3522 /* Check for user-mode parameters */
3523 if (PreviousMode != KernelMode)
3524 {
3525 /* Enter SEH */
3526 _SEH2_TRY
3527 {
3528 /* Probe and capture the maximum size, it's both read and write */
3529 ProbeForWriteLargeInteger(NewMaximumSize);
3530 SafeNewMaximumSize = *NewMaximumSize;
3531 }
3532 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3533 {
3534 /* Return the exception code */
3535 _SEH2_YIELD(return _SEH2_GetExceptionCode());
3536 }
3537 _SEH2_END;
3538 }
3539 else
3540 {
3541 /* Just read the size directly */
3542 SafeNewMaximumSize = *NewMaximumSize;
3543 }
3544
3545 /* Reference the section */
3546 Status = ObReferenceObjectByHandle(SectionHandle,
3547 SECTION_EXTEND_SIZE,
3548 MmSectionObjectType,
3549 PreviousMode,
3550 (PVOID*)&Section,
3551 NULL);
3552 if (!NT_SUCCESS(Status)) return Status;
3553
3554 /* Really this should go in MmExtendSection */
3555 if (!(Section->AllocationAttributes & SEC_FILE))
3556 {
3557 DPRINT1("Not extending a file\n");
3558 ObDereferenceObject(Section);
3559 return STATUS_SECTION_NOT_EXTENDED;
3560 }
3561
3562 /* FIXME: Do the work */
3563
3564 /* Dereference the section */
3565 ObDereferenceObject(Section);
3566
3567 /* Enter SEH */
3568 _SEH2_TRY
3569 {
3570 /* Write back the new size */
3571 *NewMaximumSize = SafeNewMaximumSize;
3572 }
3573 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3574 {
3575 /* Nothing to do */
3576 }
3577 _SEH2_END;
3578
3579 /* Return the status */
3580 return STATUS_NOT_IMPLEMENTED;
3581 }
3582
3583 /* EOF */