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