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