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