xref: /reactos/ntoskrnl/mm/i386/page.c (revision 83e13630)
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 managment 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 MmDeleteVirtualMapping(
238     _In_opt_ PEPROCESS Process,
239     _In_ PVOID Address,
240     _Out_opt_ BOOLEAN* WasDirty,
241     _Out_opt_ PPFN_NUMBER Page)
242 {
243     PMMPTE PointerPte;
244     MMPTE OldPte;
245     BOOLEAN ValidPde;
246 
247     OldPte.u.Long = 0;
248 
249     DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", Process, Address, WasDirty, Page);
250 
251     ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
252 
253     /* And we should be at low IRQL */
254     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
255 
256     /* Make sure our PDE is valid, and that everything is going fine */
257     if (Process == NULL)
258     {
259         if (Address < MmSystemRangeStart)
260         {
261             DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
262             KeBugCheck(MEMORY_MANAGEMENT);
263         }
264 #if (_MI_PAGING_LEVELS == 2)
265         ValidPde = MiSynchronizeSystemPde(MiAddressToPde(Address));
266 #else
267         ValidPde = MiIsPdeForAddressValid(Address);
268 #endif
269     }
270     else
271     {
272         if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
273         {
274             DPRINT1("Process %p given for kernel-mode mapping at %p\n", Process, Address);
275             KeBugCheck(MEMORY_MANAGEMENT);
276         }
277 
278         /* Only for current process !!! */
279         ASSERT(Process == PsGetCurrentProcess());
280         MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
281 
282         ValidPde = MiIsPageTablePresent(Address);
283         if (ValidPde)
284         {
285             MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
286         }
287     }
288 
289     /* Get the PTE if we're having anything */
290     if (ValidPde)
291     {
292         PointerPte = MiAddressToPte(Address);
293         OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
294 
295         KeInvalidateTlbEntry(Address);
296 
297         if (OldPte.u.Long != 0)
298         {
299             /* It must have been present, or not a swap entry */
300             ASSERT(OldPte.u.Hard.Valid || !FlagOn(OldPte.u.Long, 0x800));
301             if (WasDirty != NULL)
302             {
303                 *WasDirty = !!OldPte.u.Hard.Dirty;
304             }
305             if (Page != NULL)
306             {
307                 *Page = OldPte.u.Hard.PageFrameNumber;
308             }
309         }
310     }
311 
312     if (Process != NULL)
313     {
314         /* Remove PDE reference, if needed */
315         if (OldPte.u.Long != 0)
316         {
317             if (MiDecrementPageTableReferences(Address) == 0)
318             {
319                 KIRQL OldIrql = MiAcquirePfnLock();
320                 MiDeletePde(MiAddressToPde(Address), Process);
321                 MiReleasePfnLock(OldIrql);
322             }
323         }
324 
325         MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
326     }
327 
328     return OldPte.u.Long != 0;
329 }
330 
331 
332 VOID
333 NTAPI
334 MmDeletePageFileMapping(
335     PEPROCESS Process,
336     PVOID Address,
337     SWAPENTRY* SwapEntry)
338 {
339     PMMPTE PointerPte;
340     MMPTE OldPte;
341 
342     /* This should not be called for kernel space anymore */
343     ASSERT(Process != NULL);
344     ASSERT(Address < MmSystemRangeStart);
345 
346     /* And we don't support deleting for other process */
347     ASSERT(Process == PsGetCurrentProcess());
348 
349     /* And we should be at low IRQL */
350     ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
351 
352     /* We are tinkering with the PDE here. Ensure it will be there */
353     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
354 
355     /* Callers must ensure there is actually something there */
356     ASSERT(MiAddressToPde(Address)->u.Long != 0);
357 
358     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
359 
360     PointerPte = MiAddressToPte(Address);
361     OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
362     /* This must be a swap entry ! */
363     if (!FlagOn(OldPte.u.Long, 0x800) || OldPte.u.Hard.Valid)
364     {
365         KeBugCheckEx(MEMORY_MANAGEMENT, OldPte.u.Long, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
366     }
367 
368     /* This used to be a non-zero PTE, now we can let the PDE go. */
369     if (MiDecrementPageTableReferences(Address) == 0)
370     {
371         /* We can let it go */
372         KIRQL OldIrql = MiAcquirePfnLock();
373         MiDeletePde(MiPteToPde(PointerPte), Process);
374         MiReleasePfnLock(OldIrql);
375     }
376 
377     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
378 
379     *SwapEntry = OldPte.u.Long >> 1;
380 }
381 
382 BOOLEAN
383 NTAPI
384 MmIsPagePresent(PEPROCESS Process, PVOID Address)
385 {
386     BOOLEAN Ret;
387 
388     if (Address >= MmSystemRangeStart)
389     {
390         ASSERT(Process == NULL);
391 #if _MI_PAGING_LEVELS == 2
392         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
393 #else
394         if (!MiIsPdeForAddressValid(Address))
395 #endif
396         {
397             /* It can't be present if there is no PDE */
398             return FALSE;
399         }
400 
401         return MiAddressToPte(Address)->u.Hard.Valid;
402     }
403 
404     ASSERT(Process != NULL);
405     ASSERT(Process == PsGetCurrentProcess());
406 
407     MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
408 
409     if (!MiIsPageTablePresent(Address))
410     {
411         /* It can't be present if there is no PDE */
412         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
413         return FALSE;
414     }
415 
416     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
417 
418     Ret = MiAddressToPte(Address)->u.Hard.Valid;
419 
420     MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
421 
422     return Ret;
423 }
424 
425 BOOLEAN
426 NTAPI
427 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
428 {
429     BOOLEAN Ret;
430     PMMPTE PointerPte;
431 
432     if (Address >= MmSystemRangeStart)
433     {
434         ASSERT(Process == NULL);
435 #if _MI_PAGING_LEVELS == 2
436         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
437 #else
438         if (!MiIsPdeForAddressValid(Address))
439 #endif
440         {
441             /* It's not disabled if it's not present */
442             return FALSE;
443         }
444     }
445     else
446     {
447         ASSERT(Process != NULL);
448         ASSERT(Process == PsGetCurrentProcess());
449 
450         MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
451 
452         if (!MiIsPageTablePresent(Address))
453         {
454             /* It can't be disabled if there is no PDE */
455             MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
456             return FALSE;
457         }
458 
459         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
460     }
461 
462     PointerPte = MiAddressToPte(Address);
463     Ret = !PointerPte->u.Hard.Valid
464         && !FlagOn(PointerPte->u.Long, 0x800)
465         && (PointerPte->u.Hard.PageFrameNumber != 0);
466 
467     if (Address < MmSystemRangeStart)
468         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
469 
470     return Ret;
471 }
472 
473 BOOLEAN
474 NTAPI
475 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
476 {
477     BOOLEAN Ret;
478     PMMPTE PointerPte;
479 
480     /* We never set swap entries for kernel addresses */
481     if (Address >= MmSystemRangeStart)
482     {
483         ASSERT(Process == NULL);
484         return FALSE;
485     }
486 
487     ASSERT(Process != NULL);
488     ASSERT(Process == PsGetCurrentProcess());
489 
490     MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
491 
492     if (!MiIsPageTablePresent(Address))
493     {
494         /* There can't be a swap entry if there is no PDE */
495         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
496         return FALSE;
497     }
498 
499     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
500 
501     PointerPte = MiAddressToPte(Address);
502     Ret = !PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800);
503 
504     MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
505 
506     return Ret;
507 }
508 
509 VOID
510 NTAPI
511 MmGetPageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY* SwapEntry)
512 {
513     PMMPTE PointerPte;
514 
515     /* We never set swap entries for kernel addresses */
516     if (Address >= MmSystemRangeStart)
517     {
518         ASSERT(Process == NULL);
519         *SwapEntry = 0;
520         return;
521     }
522 
523     ASSERT(Process != NULL);
524     ASSERT(Process == PsGetCurrentProcess());
525 
526     MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
527 
528     if (!MiIsPageTablePresent(Address))
529     {
530         /* There can't be a swap entry if there is no PDE */
531         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
532         *SwapEntry = 0;
533         return;
534     }
535 
536     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
537 
538     PointerPte = MiAddressToPte(Address);
539     if (!PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800))
540         *SwapEntry = PointerPte->u.Long >> 1;
541     else
542         *SwapEntry = 0;
543 
544     MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
545 }
546 
547 NTSTATUS
548 NTAPI
549 MmCreatePageFileMapping(PEPROCESS Process,
550                         PVOID Address,
551                         SWAPENTRY SwapEntry)
552 {
553     PMMPTE PointerPte;
554     ULONG_PTR Pte;
555 
556     /* This should not be called for kernel space anymore */
557     ASSERT(Process != NULL);
558     ASSERT(Address < MmSystemRangeStart);
559 
560     /* And we don't support creating for other process */
561     ASSERT(Process == PsGetCurrentProcess());
562 
563     if (SwapEntry & (1 << 31))
564     {
565         KeBugCheck(MEMORY_MANAGEMENT);
566     }
567 
568     /* We are tinkering with the PDE here. Ensure it will be there */
569     ASSERT(Process == PsGetCurrentProcess());
570     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
571 
572     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
573 
574     PointerPte = MiAddressToPte(Address);
575     Pte = InterlockedExchangePte(PointerPte, SwapEntry << 1);
576     if (Pte != 0)
577     {
578         KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
579     }
580 
581     /* This used to be a 0 PTE, now we need a valid PDE to keep it around */
582     MiIncrementPageTableReferences(Address);
583     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
584 
585     return STATUS_SUCCESS;
586 }
587 
588 
589 NTSTATUS
590 NTAPI
591 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
592                              PVOID Address,
593                              ULONG flProtect,
594                              PFN_NUMBER Page)
595 {
596     ULONG ProtectionMask;
597     PMMPTE PointerPte;
598     MMPTE TempPte;
599     ULONG_PTR Pte;
600 
601     DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %x)\n",
602            Process, Address, flProtect, Page);
603 
604     ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
605 
606     ProtectionMask = MiMakeProtectionMask(flProtect);
607     /* Caller must have checked ! */
608     ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
609     ASSERT(ProtectionMask != MM_NOACCESS);
610     ASSERT(ProtectionMask != MM_ZERO_ACCESS);
611 
612     /* Make sure our PDE is valid, and that everything is going fine */
613     if (Process == NULL)
614     {
615         /* We don't support this in legacy Mm for kernel mappings */
616         ASSERT(ProtectionMask != MM_WRITECOPY);
617         ASSERT(ProtectionMask != MM_EXECUTE_WRITECOPY);
618 
619         if (Address < MmSystemRangeStart)
620         {
621             DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
622             KeBugCheck(MEMORY_MANAGEMENT);
623         }
624 #if _MI_PAGING_LEVELS == 2
625         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
626             MiFillSystemPageDirectory(Address, PAGE_SIZE);
627 #endif
628     }
629     else
630     {
631         if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
632         {
633             DPRINT1("Process %p given for kernel-mode mapping at %p -- 1 page starting at %Ix\n",
634                     Process, Address, Page);
635             KeBugCheck(MEMORY_MANAGEMENT);
636         }
637 
638         /* Only for current process !!! */
639         ASSERT(Process == PsGetCurrentProcess());
640         MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
641 
642         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
643     }
644 
645     PointerPte = MiAddressToPte(Address);
646 
647     MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, ProtectionMask, Page);
648 
649     Pte = InterlockedExchangePte(PointerPte, TempPte.u.Long);
650     /* There should not have been anything valid here */
651     if (Pte != 0)
652     {
653         DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address);
654         KeBugCheck(MEMORY_MANAGEMENT);
655     }
656 
657     /* We don't need to flush the TLB here because it only caches valid translations
658      * and we're moving this PTE from invalid to valid so it can't be cached right now */
659 
660     if (Address < MmSystemRangeStart)
661     {
662         /* Add PDE reference */
663         MiIncrementPageTableReferences(Address);
664         MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
665     }
666 
667     return(STATUS_SUCCESS);
668 }
669 
670 NTSTATUS
671 NTAPI
672 MmCreateVirtualMapping(PEPROCESS Process,
673                        PVOID Address,
674                        ULONG flProtect,
675                        PFN_NUMBER Page)
676 {
677     ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
678     if (!MmIsPageInUse(Page))
679     {
680         DPRINT1("Page %lx is not in use\n", Page);
681         KeBugCheck(MEMORY_MANAGEMENT);
682     }
683 
684     return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page);
685 }
686 
687 ULONG
688 NTAPI
689 MmGetPageProtect(PEPROCESS Process, PVOID Address)
690 {
691     PMMPTE PointerPte;
692     ULONG Protect;
693 
694     if (Address >= MmSystemRangeStart)
695     {
696         ASSERT(Process == NULL);
697 
698 #if _MI_PAGING_LEVELS == 2
699         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
700 #else
701         if (!MiIsPdeForAddressValid(Address))
702 #endif
703         {
704             return PAGE_NOACCESS;
705         }
706     }
707     else
708     {
709         ASSERT(Address < MmSystemRangeStart);
710         ASSERT(Process != NULL);
711 
712         ASSERT(Process == PsGetCurrentProcess());
713 
714         MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
715 
716         if (!MiIsPageTablePresent(Address))
717         {
718             /* It can't be present if there is no PDE */
719             MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
720             return PAGE_NOACCESS;
721         }
722 
723         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
724     }
725 
726     PointerPte = MiAddressToPte(Address);
727 
728     if (!PointerPte->u.Flush.Valid)
729     {
730         Protect = PAGE_NOACCESS;
731     }
732     else
733     {
734         if (PointerPte->u.Flush.CopyOnWrite)
735             Protect = PAGE_WRITECOPY;
736         else if (PointerPte->u.Flush.Write)
737             Protect = PAGE_READWRITE;
738         else
739             Protect = PAGE_READONLY;
740 #if _MI_PAGING_LEVELS >= 3
741         /* PAE & AMD64 long mode support NoExecute bit */
742         if (!PointerPte->u.Flush.NoExecute)
743             Protect <<= 4;
744 #endif
745         if (PointerPte->u.Flush.CacheDisable)
746             Protect |= PAGE_NOCACHE;
747         if (PointerPte->u.Flush.WriteThrough)
748             Protect |= PAGE_WRITETHROUGH;
749     }
750 
751     if (Address < MmSystemRangeStart)
752         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
753 
754     return(Protect);
755 }
756 
757 VOID
758 NTAPI
759 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
760 {
761     ULONG ProtectionMask;
762     PMMPTE PointerPte;
763     MMPTE TempPte, OldPte;
764 
765     DPRINT("MmSetPageProtect(Process %p  Address %p  flProtect %x)\n",
766            Process, Address, flProtect);
767 
768     ASSERT(Process != NULL);
769     ASSERT(Address < MmSystemRangeStart);
770 
771     ASSERT(Process == PsGetCurrentProcess());
772 
773     ProtectionMask = MiMakeProtectionMask(flProtect);
774     /* Caller must have checked ! */
775     ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
776 
777     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
778 
779     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
780 
781     PointerPte = MiAddressToPte(Address);
782 
783     /* Sanity check */
784     ASSERT(PointerPte->u.Hard.Owner == 1);
785 
786     TempPte.u.Long = 0;
787     TempPte.u.Hard.PageFrameNumber = PointerPte->u.Hard.PageFrameNumber;
788     TempPte.u.Long |= MmProtectToPteMask[ProtectionMask];
789     TempPte.u.Hard.Owner = 1;
790 
791     /* Only set valid bit if we have to */
792     if ((ProtectionMask != MM_NOACCESS) && !FlagOn(ProtectionMask, MM_GUARDPAGE))
793         TempPte.u.Hard.Valid = 1;
794 
795     /* Keep dirty & accessed bits */
796     TempPte.u.Hard.Accessed = PointerPte->u.Hard.Accessed;
797     TempPte.u.Hard.Dirty = PointerPte->u.Hard.Dirty;
798 
799     OldPte.u.Long = InterlockedExchangePte(PointerPte, TempPte.u.Long);
800 
801     // We should be able to bring a page back from PAGE_NOACCESS
802     if (!OldPte.u.Hard.Valid && (FlagOn(OldPte.u.Long, 0x800) || (OldPte.u.Hard.PageFrameNumber == 0)))
803     {
804         DPRINT1("Invalid Pte %lx\n", OldPte.u.Long);
805         KeBugCheck(MEMORY_MANAGEMENT);
806     }
807 
808     if (OldPte.u.Long != TempPte.u.Long)
809         KeInvalidateTlbEntry(Address);
810 
811     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
812 }
813 
814 VOID
815 NTAPI
816 MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit)
817 {
818     PMMPTE PointerPte;
819 
820     DPRINT("MmSetDirtyBit(Process %p  Address %p  Bit %x)\n",
821            Process, Address, Bit);
822 
823     ASSERT(Process != NULL);
824     ASSERT(Address < MmSystemRangeStart);
825 
826     ASSERT(Process == PsGetCurrentProcess());
827 
828     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
829 
830     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
831 
832     PointerPte = MiAddressToPte(Address);
833     // We shouldnl't set dirty bit on non-mapped adresses
834     if (!PointerPte->u.Hard.Valid && (FlagOn(PointerPte->u.Long, 0x800) || (PointerPte->u.Hard.PageFrameNumber == 0)))
835     {
836         DPRINT1("Invalid Pte %lx\n", PointerPte->u.Long);
837         KeBugCheck(MEMORY_MANAGEMENT);
838     }
839 
840     PointerPte->u.Hard.Dirty = !!Bit;
841 
842     if (!Bit)
843         KeInvalidateTlbEntry(Address);
844 
845     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
846 }
847 
848 CODE_SEG("INIT")
849 VOID
850 NTAPI
851 MmInitGlobalKernelPageDirectory(VOID)
852 {
853     /* Nothing to do here */
854 }
855 
856 #ifdef _M_IX86
857 BOOLEAN
858 Mmi386MakeKernelPageTableGlobal(PVOID Address)
859 {
860     PMMPDE PointerPde = MiAddressToPde(Address);
861     PMMPTE PointerPte = MiAddressToPte(Address);
862 
863     if (PointerPde->u.Hard.Valid == 0)
864     {
865         if (!MiSynchronizeSystemPde(PointerPde))
866             return FALSE;
867         return PointerPte->u.Hard.Valid != 0;
868     }
869     return FALSE;
870 }
871 #endif
872 
873 /* EOF */
874