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