xref: /reactos/ntoskrnl/mm/i386/page.c (revision bea6d763)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS kernel
4  * FILE:            ntoskrnl/mm/i386/page.c
5  * PURPOSE:         Low level memory management manipulation
6  *
7  * PROGRAMMERS:     David Welch (welch@cwcom.net)
8  */
9 
10 /* INCLUDES ***************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #include <mm/ARM3/miarm.h>
17 
18 #ifndef _MI_PAGING_LEVELS
19 #error "Dude, fix your stuff before using this file"
20 #endif
21 
22 /* GLOBALS *****************************************************************/
23 const
24 ULONG_PTR
25 MmProtectToPteMask[32] =
26 {
27     //
28     // These are the base MM_ protection flags
29     //
30     0,
31     PTE_READONLY            | PTE_ENABLE_CACHE,
32     PTE_EXECUTE             | PTE_ENABLE_CACHE,
33     PTE_EXECUTE_READ        | PTE_ENABLE_CACHE,
34     PTE_READWRITE           | PTE_ENABLE_CACHE,
35     PTE_WRITECOPY           | PTE_ENABLE_CACHE,
36     PTE_EXECUTE_READWRITE   | PTE_ENABLE_CACHE,
37     PTE_EXECUTE_WRITECOPY   | PTE_ENABLE_CACHE,
38     //
39     // These OR in the MM_NOCACHE flag
40     //
41     0,
42     PTE_READONLY            | PTE_DISABLE_CACHE,
43     PTE_EXECUTE             | PTE_DISABLE_CACHE,
44     PTE_EXECUTE_READ        | PTE_DISABLE_CACHE,
45     PTE_READWRITE           | PTE_DISABLE_CACHE,
46     PTE_WRITECOPY           | PTE_DISABLE_CACHE,
47     PTE_EXECUTE_READWRITE   | PTE_DISABLE_CACHE,
48     PTE_EXECUTE_WRITECOPY   | PTE_DISABLE_CACHE,
49     //
50     // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
51     //
52     0,
53     PTE_READONLY            | PTE_ENABLE_CACHE,
54     PTE_EXECUTE             | PTE_ENABLE_CACHE,
55     PTE_EXECUTE_READ        | PTE_ENABLE_CACHE,
56     PTE_READWRITE           | PTE_ENABLE_CACHE,
57     PTE_WRITECOPY           | PTE_ENABLE_CACHE,
58     PTE_EXECUTE_READWRITE   | PTE_ENABLE_CACHE,
59     PTE_EXECUTE_WRITECOPY   | PTE_ENABLE_CACHE,
60     //
61     // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
62     //
63     0,
64     PTE_READONLY            | PTE_WRITECOMBINED_CACHE,
65     PTE_EXECUTE             | PTE_WRITECOMBINED_CACHE,
66     PTE_EXECUTE_READ        | PTE_WRITECOMBINED_CACHE,
67     PTE_READWRITE           | PTE_WRITECOMBINED_CACHE,
68     PTE_WRITECOPY           | PTE_WRITECOMBINED_CACHE,
69     PTE_EXECUTE_READWRITE   | PTE_WRITECOMBINED_CACHE,
70     PTE_EXECUTE_WRITECOPY   | PTE_WRITECOMBINED_CACHE,
71 };
72 
73 const
74 ULONG MmProtectToValue[32] =
75 {
76     PAGE_NOACCESS,
77     PAGE_READONLY,
78     PAGE_EXECUTE,
79     PAGE_EXECUTE_READ,
80     PAGE_READWRITE,
81     PAGE_WRITECOPY,
82     PAGE_EXECUTE_READWRITE,
83     PAGE_EXECUTE_WRITECOPY,
84     PAGE_NOACCESS,
85     PAGE_NOCACHE | PAGE_READONLY,
86     PAGE_NOCACHE | PAGE_EXECUTE,
87     PAGE_NOCACHE | PAGE_EXECUTE_READ,
88     PAGE_NOCACHE | PAGE_READWRITE,
89     PAGE_NOCACHE | PAGE_WRITECOPY,
90     PAGE_NOCACHE | PAGE_EXECUTE_READWRITE,
91     PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY,
92     PAGE_NOACCESS,
93     PAGE_GUARD | PAGE_READONLY,
94     PAGE_GUARD | PAGE_EXECUTE,
95     PAGE_GUARD | PAGE_EXECUTE_READ,
96     PAGE_GUARD | PAGE_READWRITE,
97     PAGE_GUARD | PAGE_WRITECOPY,
98     PAGE_GUARD | PAGE_EXECUTE_READWRITE,
99     PAGE_GUARD | PAGE_EXECUTE_WRITECOPY,
100     PAGE_NOACCESS,
101     PAGE_WRITECOMBINE | PAGE_READONLY,
102     PAGE_WRITECOMBINE | PAGE_EXECUTE,
103     PAGE_WRITECOMBINE | PAGE_EXECUTE_READ,
104     PAGE_WRITECOMBINE | PAGE_READWRITE,
105     PAGE_WRITECOMBINE | PAGE_WRITECOPY,
106     PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE,
107     PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY
108 };
109 
110 /* FUNCTIONS ***************************************************************/
111 
112 NTSTATUS
113 NTAPI
114 MiFillSystemPageDirectory(IN PVOID Base,
115                           IN SIZE_T NumberOfBytes);
116 
117 static
118 BOOLEAN
119 MiIsPageTablePresent(PVOID Address)
120 {
121 #if _MI_PAGING_LEVELS == 2
122     BOOLEAN Ret = MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] != 0;
123 
124     /* Some sanity check while we're here */
125     ASSERT(Ret == (MiAddressToPde(Address)->u.Hard.Valid != 0));
126 
127     return Ret;
128 #else
129     PMMPDE PointerPde;
130     PMMPPE PointerPpe;
131 #if _MI_PAGING_LEVELS == 4
132     PMMPXE PointerPxe;
133 #endif
134     PMMPFN Pfn;
135 
136     /* Make sure we're locked */
137     ASSERT((PsGetCurrentThread()->OwnsProcessWorkingSetExclusive) || (PsGetCurrentThread()->OwnsProcessWorkingSetShared));
138 
139     /* Must not hold the PFN lock! */
140     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
141 
142         /* Check if PXE or PPE have references first. */
143 #if _MI_PAGING_LEVELS == 4
144     PointerPxe = MiAddressToPxe(Address);
145     if ((PointerPxe->u.Hard.Valid == 1) || (PointerPxe->u.Soft.Transition == 1))
146     {
147         Pfn = MiGetPfnEntry(PFN_FROM_PXE(PointerPxe));
148         if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
149             return FALSE;
150     }
151     else if (PointerPxe->u.Soft.UsedPageTableEntries == 0)
152     {
153         return FALSE;
154     }
155 
156     if (PointerPxe->u.Hard.Valid == 0)
157     {
158         MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), PsGetCurrentProcess());
159     }
160 #endif
161 
162     PointerPpe = MiAddressToPpe(Address);
163     if ((PointerPpe->u.Hard.Valid == 1) || (PointerPpe->u.Soft.Transition == 1))
164     {
165         Pfn = MiGetPfnEntry(PFN_FROM_PPE(PointerPpe));
166         if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
167             return FALSE;
168     }
169     else if (PointerPpe->u.Soft.UsedPageTableEntries == 0)
170     {
171         return FALSE;
172     }
173 
174     if (PointerPpe->u.Hard.Valid == 0)
175     {
176         MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), PsGetCurrentProcess());
177     }
178 
179     PointerPde = MiAddressToPde(Address);
180     if ((PointerPde->u.Hard.Valid == 0) && (PointerPde->u.Soft.Transition == 0))
181     {
182         return PointerPde->u.Soft.UsedPageTableEntries != 0;
183     }
184 
185     /* This lies on the PFN */
186     Pfn = MiGetPfnEntry(PFN_FROM_PDE(PointerPde));
187     return Pfn->OriginalPte.u.Soft.UsedPageTableEntries != 0;
188 #endif
189 }
190 
191 PFN_NUMBER
192 NTAPI
193 MmGetPfnForProcess(PEPROCESS Process,
194                    PVOID Address)
195 {
196     PMMPTE PointerPte;
197     PFN_NUMBER Page;
198 
199     /* Must be called for user mode only */
200     ASSERT(Process != NULL);
201     ASSERT(Address < MmSystemRangeStart);
202 
203     /* And for our process */
204     ASSERT(Process == PsGetCurrentProcess());
205 
206     /* Lock for reading */
207     MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
208 
209     if (!MiIsPageTablePresent(Address))
210     {
211         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
212         return 0;
213     }
214 
215     /* Make sure we can read the PTE */
216     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
217 
218     PointerPte = MiAddressToPte(Address);
219     Page = PointerPte->u.Hard.Valid ? PFN_FROM_PTE(PointerPte) : 0;
220 
221     MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
222     return Page;
223 }
224 
225 /**
226  * @brief Deletes the virtual mapping and optionally gives back the page & dirty bit.
227  *
228  * @param Process - The process this address belongs to, or NULL if system address.
229  * @param Address - The address to unmap.
230  * @param WasDirty - Optional param receiving the dirty bit of the PTE.
231  * @param Page - Optional param receiving the page number previously mapped to this address.
232  *
233  * @return Whether there was actually a page mapped at the given address.
234  */
235 _Success_(return)
236 BOOLEAN
237 MmDeleteVirtualMappingEx(
238     _Inout_opt_ PEPROCESS Process,
239     _In_ PVOID Address,
240     _Out_opt_ BOOLEAN* WasDirty,
241     _Out_opt_ PPFN_NUMBER Page,
242     _In_ BOOLEAN IsPhysical)
243 {
244     PMMPTE PointerPte;
245     MMPTE OldPte;
246     BOOLEAN ValidPde;
247 
248     OldPte.u.Long = 0;
249 
250     DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", Process, Address, WasDirty, Page);
251 
252     ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
253 
254     /* And we should be at low IRQL */
255     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
256 
257     /* Make sure our PDE is valid, and that everything is going fine */
258     if (Process == NULL)
259     {
260         if (Address < MmSystemRangeStart)
261         {
262             DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
263             KeBugCheck(MEMORY_MANAGEMENT);
264         }
265 #if (_MI_PAGING_LEVELS == 2)
266         ValidPde = MiSynchronizeSystemPde(MiAddressToPde(Address));
267 #else
268         ValidPde = MiIsPdeForAddressValid(Address);
269 #endif
270     }
271     else
272     {
273         if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
274         {
275             DPRINT1("Process %p given for kernel-mode mapping at %p\n", Process, Address);
276             KeBugCheck(MEMORY_MANAGEMENT);
277         }
278 
279         /* Only for current process !!! */
280         ASSERT(Process == PsGetCurrentProcess());
281         MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
282 
283         ValidPde = MiIsPageTablePresent(Address);
284         if (ValidPde)
285         {
286             MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
287         }
288     }
289 
290     /* Get the PTE if we're having anything */
291     if (ValidPde)
292     {
293         PointerPte = MiAddressToPte(Address);
294         OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
295 
296         KeInvalidateTlbEntry(Address);
297 
298         if (OldPte.u.Long != 0)
299         {
300             /* It must have been present, or not a swap entry */
301             ASSERT(OldPte.u.Hard.Valid || !FlagOn(OldPte.u.Long, 0x800));
302             if (WasDirty != NULL)
303             {
304                 *WasDirty = !!OldPte.u.Hard.Dirty;
305             }
306             if (Page != NULL)
307             {
308                 *Page = OldPte.u.Hard.PageFrameNumber;
309             }
310         }
311     }
312 
313     if (Process != NULL)
314     {
315         /* Remove PDE reference, if needed */
316         if (OldPte.u.Long != 0)
317         {
318             if (MiDecrementPageTableReferences(Address) == 0)
319             {
320                 KIRQL OldIrql = MiAcquirePfnLock();
321                 MiDeletePde(MiAddressToPde(Address), Process);
322                 MiReleasePfnLock(OldIrql);
323             }
324         }
325 
326         if (!IsPhysical && OldPte.u.Hard.Valid)
327         {
328             PMMPFN Pfn1;
329             KIRQL OldIrql;
330 
331             OldIrql = MiAcquirePfnLock();
332             Pfn1 = &MmPfnDatabase[OldPte.u.Hard.PageFrameNumber];
333             ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
334             ASSERT(Pfn1->u2.ShareCount > 0);
335             if (--Pfn1->u2.ShareCount == 0)
336             {
337                 Pfn1->u3.e1.PageLocation = TransitionPage;
338             }
339             MiReleasePfnLock(OldIrql);
340         }
341 
342         MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
343     }
344 
345     return OldPte.u.Long != 0;
346 }
347 
348 _Success_(return)
349 BOOLEAN
350 MmDeleteVirtualMapping(
351     _Inout_opt_ PEPROCESS Process,
352     _In_ PVOID Address,
353     _Out_opt_ BOOLEAN * WasDirty,
354     _Out_opt_ PPFN_NUMBER Page)
355 {
356     return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, FALSE);
357 }
358 
359 _Success_(return)
360 BOOLEAN
361 MmDeletePhysicalMapping(
362     _Inout_opt_ PEPROCESS Process,
363     _In_ PVOID Address,
364     _Out_opt_ BOOLEAN * WasDirty,
365     _Out_opt_ PPFN_NUMBER Page)
366 {
367     return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, TRUE);
368 }
369 
370 VOID
371 NTAPI
372 MmDeletePageFileMapping(
373     PEPROCESS Process,
374     PVOID Address,
375     SWAPENTRY* SwapEntry)
376 {
377     PMMPTE PointerPte;
378     MMPTE OldPte;
379 
380     /* This should not be called for kernel space anymore */
381     ASSERT(Process != NULL);
382     ASSERT(Address < MmSystemRangeStart);
383 
384     /* And we don't support deleting for other process */
385     ASSERT(Process == PsGetCurrentProcess());
386 
387     /* And we should be at low IRQL */
388     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
389 
390     /* We are tinkering with the PDE here. Ensure it will be there */
391     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
392 
393     /* Callers must ensure there is actually something there */
394     ASSERT(MiAddressToPde(Address)->u.Long != 0);
395 
396     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
397 
398     PointerPte = MiAddressToPte(Address);
399     OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
400     /* This must be a swap entry ! */
401     if (!FlagOn(OldPte.u.Long, 0x800) || OldPte.u.Hard.Valid)
402     {
403         KeBugCheckEx(MEMORY_MANAGEMENT, OldPte.u.Long, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
404     }
405 
406     /* This used to be a non-zero PTE, now we can let the PDE go. */
407     if (MiDecrementPageTableReferences(Address) == 0)
408     {
409         /* We can let it go */
410         KIRQL OldIrql = MiAcquirePfnLock();
411         MiDeletePde(MiPteToPde(PointerPte), Process);
412         MiReleasePfnLock(OldIrql);
413     }
414 
415     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
416 
417     *SwapEntry = OldPte.u.Long >> 1;
418 }
419 
420 BOOLEAN
421 NTAPI
422 MmIsPagePresent(PEPROCESS Process, PVOID Address)
423 {
424     BOOLEAN Ret;
425 
426     if (Address >= MmSystemRangeStart)
427     {
428         ASSERT(Process == NULL);
429 #if _MI_PAGING_LEVELS == 2
430         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
431 #else
432         if (!MiIsPdeForAddressValid(Address))
433 #endif
434         {
435             /* It can't be present if there is no PDE */
436             return FALSE;
437         }
438 
439         return MiAddressToPte(Address)->u.Hard.Valid;
440     }
441 
442     ASSERT(Process != NULL);
443     ASSERT(Process == PsGetCurrentProcess());
444 
445     MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
446 
447     if (!MiIsPageTablePresent(Address))
448     {
449         /* It can't be present if there is no PDE */
450         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
451         return FALSE;
452     }
453 
454     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
455 
456     Ret = MiAddressToPte(Address)->u.Hard.Valid;
457 
458     MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
459 
460     return Ret;
461 }
462 
463 BOOLEAN
464 NTAPI
465 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
466 {
467     BOOLEAN Ret;
468     PMMPTE PointerPte;
469 
470     if (Address >= MmSystemRangeStart)
471     {
472         ASSERT(Process == NULL);
473 #if _MI_PAGING_LEVELS == 2
474         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
475 #else
476         if (!MiIsPdeForAddressValid(Address))
477 #endif
478         {
479             /* It's not disabled if it's not present */
480             return FALSE;
481         }
482     }
483     else
484     {
485         ASSERT(Process != NULL);
486         ASSERT(Process == PsGetCurrentProcess());
487 
488         MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
489 
490         if (!MiIsPageTablePresent(Address))
491         {
492             /* It can't be disabled if there is no PDE */
493             MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
494             return FALSE;
495         }
496 
497         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
498     }
499 
500     PointerPte = MiAddressToPte(Address);
501     Ret = !PointerPte->u.Hard.Valid
502         && !FlagOn(PointerPte->u.Long, 0x800)
503         && (PointerPte->u.Hard.PageFrameNumber != 0);
504 
505     if (Address < MmSystemRangeStart)
506         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
507 
508     return Ret;
509 }
510 
511 BOOLEAN
512 NTAPI
513 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
514 {
515     BOOLEAN Ret;
516     PMMPTE PointerPte;
517 
518     /* We never set swap entries for kernel addresses */
519     if (Address >= MmSystemRangeStart)
520     {
521         ASSERT(Process == NULL);
522         return FALSE;
523     }
524 
525     ASSERT(Process != NULL);
526     ASSERT(Process == PsGetCurrentProcess());
527 
528     MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
529 
530     if (!MiIsPageTablePresent(Address))
531     {
532         /* There can't be a swap entry if there is no PDE */
533         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
534         return FALSE;
535     }
536 
537     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
538 
539     PointerPte = MiAddressToPte(Address);
540     Ret = !PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800);
541 
542     MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
543 
544     return Ret;
545 }
546 
547 VOID
548 NTAPI
549 MmGetPageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY* SwapEntry)
550 {
551     PMMPTE PointerPte;
552 
553     /* We never set swap entries for kernel addresses */
554     if (Address >= MmSystemRangeStart)
555     {
556         ASSERT(Process == NULL);
557         *SwapEntry = 0;
558         return;
559     }
560 
561     ASSERT(Process != NULL);
562     ASSERT(Process == PsGetCurrentProcess());
563 
564     MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
565 
566     if (!MiIsPageTablePresent(Address))
567     {
568         /* There can't be a swap entry if there is no PDE */
569         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
570         *SwapEntry = 0;
571         return;
572     }
573 
574     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
575 
576     PointerPte = MiAddressToPte(Address);
577     if (!PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800))
578         *SwapEntry = PointerPte->u.Long >> 1;
579     else
580         *SwapEntry = 0;
581 
582     MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
583 }
584 
585 NTSTATUS
586 NTAPI
587 MmCreatePageFileMapping(PEPROCESS Process,
588                         PVOID Address,
589                         SWAPENTRY SwapEntry)
590 {
591     PMMPTE PointerPte;
592     ULONG_PTR Pte;
593 
594     /* This should not be called for kernel space anymore */
595     ASSERT(Process != NULL);
596     ASSERT(Address < MmSystemRangeStart);
597 
598     /* And we don't support creating for other process */
599     ASSERT(Process == PsGetCurrentProcess());
600 
601     if (SwapEntry & (1 << (sizeof(SWAPENTRY) - 1)))
602     {
603         KeBugCheck(MEMORY_MANAGEMENT);
604     }
605 
606     /* We are tinkering with the PDE here. Ensure it will be there */
607     ASSERT(Process == PsGetCurrentProcess());
608     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
609 
610     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
611 
612     PointerPte = MiAddressToPte(Address);
613     Pte = InterlockedExchangePte(PointerPte, SwapEntry << 1);
614     if (Pte != 0)
615     {
616         KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
617     }
618 
619     /* This used to be a 0 PTE, now we need a valid PDE to keep it around */
620     MiIncrementPageTableReferences(Address);
621     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
622 
623     return STATUS_SUCCESS;
624 }
625 
626 
627 NTSTATUS
628 NTAPI
629 MmCreateVirtualMappingUnsafeEx(
630     _Inout_opt_ PEPROCESS Process,
631     _In_ PVOID Address,
632     _In_ ULONG flProtect,
633     _In_ PFN_NUMBER Page,
634     _In_ BOOLEAN IsPhysical)
635 {
636     ULONG ProtectionMask;
637     PMMPTE PointerPte;
638     MMPTE TempPte;
639     ULONG_PTR Pte;
640 
641     DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %x)\n",
642            Process, Address, flProtect, Page);
643 
644     ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
645 
646     ProtectionMask = MiMakeProtectionMask(flProtect);
647     /* Caller must have checked ! */
648     ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
649     ASSERT(ProtectionMask != MM_NOACCESS);
650     ASSERT(ProtectionMask != MM_ZERO_ACCESS);
651 
652     /* Make sure our PDE is valid, and that everything is going fine */
653     if (Process == NULL)
654     {
655         /* We don't support this in legacy Mm for kernel mappings */
656         ASSERT(ProtectionMask != MM_WRITECOPY);
657         ASSERT(ProtectionMask != MM_EXECUTE_WRITECOPY);
658 
659         if (Address < MmSystemRangeStart)
660         {
661             DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
662             KeBugCheck(MEMORY_MANAGEMENT);
663         }
664 #if _MI_PAGING_LEVELS == 2
665         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
666             MiFillSystemPageDirectory(Address, PAGE_SIZE);
667 #endif
668     }
669     else
670     {
671         if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
672         {
673             DPRINT1("Process %p given for kernel-mode mapping at %p -- 1 page starting at %Ix\n",
674                     Process, Address, Page);
675             KeBugCheck(MEMORY_MANAGEMENT);
676         }
677 
678         /* Only for current process !!! */
679         ASSERT(Process == PsGetCurrentProcess());
680         MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
681 
682         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
683     }
684 
685     PointerPte = MiAddressToPte(Address);
686 
687     MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, ProtectionMask, Page);
688 
689     Pte = InterlockedExchangePte(PointerPte, TempPte.u.Long);
690     /* There should not have been anything valid here */
691     if (Pte != 0)
692     {
693         DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address);
694         KeBugCheck(MEMORY_MANAGEMENT);
695     }
696 
697     if (!IsPhysical)
698     {
699         PMMPFN Pfn1;
700         KIRQL OldIrql;
701 
702         OldIrql = MiAcquirePfnLock();
703         Pfn1 = &MmPfnDatabase[TempPte.u.Hard.PageFrameNumber];
704         Pfn1->u2.ShareCount++;
705         Pfn1->u3.e1.PageLocation = ActiveAndValid;
706         MiReleasePfnLock(OldIrql);
707     }
708 
709     /* We don't need to flush the TLB here because it only caches valid translations
710      * and we're moving this PTE from invalid to valid so it can't be cached right now */
711 
712     if (Address < MmSystemRangeStart)
713     {
714         /* Add PDE reference */
715         MiIncrementPageTableReferences(Address);
716         MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
717     }
718 
719     return(STATUS_SUCCESS);
720 }
721 
722 NTSTATUS
723 NTAPI
724 MmCreateVirtualMappingUnsafe(
725     _Inout_opt_ PEPROCESS Process,
726     _In_ PVOID Address,
727     _In_ ULONG flProtect,
728     _In_ PFN_NUMBER Page)
729 {
730     return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, FALSE);
731 }
732 
733 NTSTATUS
734 NTAPI
735 MmCreatePhysicalMapping(
736     _Inout_opt_ PEPROCESS Process,
737     _In_ PVOID Address,
738     _In_ ULONG flProtect,
739     _In_ PFN_NUMBER Page)
740 {
741     return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, TRUE);
742 }
743 
744 NTSTATUS
745 NTAPI
746 MmCreateVirtualMapping(PEPROCESS Process,
747                        PVOID Address,
748                        ULONG flProtect,
749                        PFN_NUMBER Page)
750 {
751     ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
752     if (!MmIsPageInUse(Page))
753     {
754         DPRINT1("Page %lx is not in use\n", Page);
755         KeBugCheck(MEMORY_MANAGEMENT);
756     }
757 
758     return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page);
759 }
760 
761 ULONG
762 NTAPI
763 MmGetPageProtect(PEPROCESS Process, PVOID Address)
764 {
765     PMMPTE PointerPte;
766     ULONG Protect;
767 
768     if (Address >= MmSystemRangeStart)
769     {
770         ASSERT(Process == NULL);
771 
772 #if _MI_PAGING_LEVELS == 2
773         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
774 #else
775         if (!MiIsPdeForAddressValid(Address))
776 #endif
777         {
778             return PAGE_NOACCESS;
779         }
780     }
781     else
782     {
783         ASSERT(Address < MmSystemRangeStart);
784         ASSERT(Process != NULL);
785 
786         ASSERT(Process == PsGetCurrentProcess());
787 
788         MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
789 
790         if (!MiIsPageTablePresent(Address))
791         {
792             /* It can't be present if there is no PDE */
793             MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
794             return PAGE_NOACCESS;
795         }
796 
797         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
798     }
799 
800     PointerPte = MiAddressToPte(Address);
801 
802     if (!PointerPte->u.Flush.Valid)
803     {
804         Protect = PAGE_NOACCESS;
805     }
806     else
807     {
808         if (PointerPte->u.Flush.CopyOnWrite)
809             Protect = PAGE_WRITECOPY;
810         else if (PointerPte->u.Flush.Write)
811             Protect = PAGE_READWRITE;
812         else
813             Protect = PAGE_READONLY;
814 #if _MI_PAGING_LEVELS >= 3
815         /* PAE & AMD64 long mode support NoExecute bit */
816         if (!PointerPte->u.Flush.NoExecute)
817             Protect <<= 4;
818 #endif
819         if (PointerPte->u.Flush.CacheDisable)
820             Protect |= PAGE_NOCACHE;
821         if (PointerPte->u.Flush.WriteThrough)
822             Protect |= PAGE_WRITETHROUGH;
823     }
824 
825     if (Address < MmSystemRangeStart)
826         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
827 
828     return(Protect);
829 }
830 
831 VOID
832 NTAPI
833 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
834 {
835     ULONG ProtectionMask;
836     PMMPTE PointerPte;
837     MMPTE TempPte, OldPte;
838 
839     DPRINT("MmSetPageProtect(Process %p  Address %p  flProtect %x)\n",
840            Process, Address, flProtect);
841 
842     ASSERT(Process != NULL);
843     ASSERT(Address < MmSystemRangeStart);
844 
845     ASSERT(Process == PsGetCurrentProcess());
846 
847     ProtectionMask = MiMakeProtectionMask(flProtect);
848     /* Caller must have checked ! */
849     ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
850 
851     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
852 
853     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
854 
855     PointerPte = MiAddressToPte(Address);
856 
857     /* Sanity check */
858     ASSERT(PointerPte->u.Hard.Owner == 1);
859 
860     TempPte.u.Long = 0;
861     TempPte.u.Hard.PageFrameNumber = PointerPte->u.Hard.PageFrameNumber;
862     TempPte.u.Long |= MmProtectToPteMask[ProtectionMask];
863     TempPte.u.Hard.Owner = 1;
864 
865     /* Only set valid bit if we have to */
866     if ((ProtectionMask != MM_NOACCESS) && !FlagOn(ProtectionMask, MM_GUARDPAGE))
867         TempPte.u.Hard.Valid = 1;
868 
869     /* Keep dirty & accessed bits */
870     TempPte.u.Hard.Accessed = PointerPte->u.Hard.Accessed;
871     TempPte.u.Hard.Dirty = PointerPte->u.Hard.Dirty;
872 
873     OldPte.u.Long = InterlockedExchangePte(PointerPte, TempPte.u.Long);
874 
875     // We should be able to bring a page back from PAGE_NOACCESS
876     if (!OldPte.u.Hard.Valid && (FlagOn(OldPte.u.Long, 0x800) || (OldPte.u.Hard.PageFrameNumber == 0)))
877     {
878         DPRINT1("Invalid Pte %lx\n", OldPte.u.Long);
879         KeBugCheck(MEMORY_MANAGEMENT);
880     }
881 
882     if (OldPte.u.Long != TempPte.u.Long)
883         KeInvalidateTlbEntry(Address);
884 
885     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
886 }
887 
888 VOID
889 NTAPI
890 MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit)
891 {
892     PMMPTE PointerPte;
893 
894     DPRINT("MmSetDirtyBit(Process %p  Address %p  Bit %x)\n",
895            Process, Address, Bit);
896 
897     ASSERT(Process != NULL);
898     ASSERT(Address < MmSystemRangeStart);
899 
900     ASSERT(Process == PsGetCurrentProcess());
901 
902     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
903 
904     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
905 
906     PointerPte = MiAddressToPte(Address);
907     // We shouldnl't set dirty bit on non-mapped adresses
908     if (!PointerPte->u.Hard.Valid && (FlagOn(PointerPte->u.Long, 0x800) || (PointerPte->u.Hard.PageFrameNumber == 0)))
909     {
910         DPRINT1("Invalid Pte %lx\n", PointerPte->u.Long);
911         KeBugCheck(MEMORY_MANAGEMENT);
912     }
913 
914     PointerPte->u.Hard.Dirty = !!Bit;
915 
916     if (!Bit)
917         KeInvalidateTlbEntry(Address);
918 
919     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
920 }
921 
922 CODE_SEG("INIT")
923 VOID
924 NTAPI
925 MmInitGlobalKernelPageDirectory(VOID)
926 {
927     /* Nothing to do here */
928 }
929 
930 #ifdef _M_IX86
931 BOOLEAN
932 Mmi386MakeKernelPageTableGlobal(PVOID Address)
933 {
934     PMMPDE PointerPde = MiAddressToPde(Address);
935     PMMPTE PointerPte = MiAddressToPte(Address);
936 
937     if (PointerPde->u.Hard.Valid == 0)
938     {
939         if (!MiSynchronizeSystemPde(PointerPde))
940             return FALSE;
941         return PointerPte->u.Hard.Valid != 0;
942     }
943     return FALSE;
944 }
945 #endif
946 
947 /* EOF */
948