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