xref: /reactos/ntoskrnl/mm/i386/page.c (revision f59c58d8)
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\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 -- 1 page starting at %Ix\n",
629                     Process, Address, Page);
630             KeBugCheck(MEMORY_MANAGEMENT);
631         }
632 
633         /* Only for current process !!! */
634         ASSERT(Process = PsGetCurrentProcess());
635         MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
636 
637         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
638     }
639 
640     PointerPte = MiAddressToPte(Address);
641 
642     MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, ProtectionMask, Page);
643 
644     Pte = InterlockedExchangePte(PointerPte, TempPte.u.Long);
645     /* There should not have been anything valid here */
646     if (Pte != 0)
647     {
648         DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address);
649         KeBugCheck(MEMORY_MANAGEMENT);
650     }
651 
652     /* We don't need to flush the TLB here because it only caches valid translations
653      * and we're moving this PTE from invalid to valid so it can't be cached right now */
654 
655     if (Address < MmSystemRangeStart)
656     {
657         /* Add PDE reference */
658         MiIncrementPageTableReferences(Address);
659         MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
660     }
661 
662     return(STATUS_SUCCESS);
663 }
664 
665 NTSTATUS
666 NTAPI
667 MmCreateVirtualMapping(PEPROCESS Process,
668                        PVOID Address,
669                        ULONG flProtect,
670                        PFN_NUMBER Page)
671 {
672     ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
673     if (!MmIsPageInUse(Page))
674     {
675         DPRINT1("Page %lx is not in use\n", Page);
676         KeBugCheck(MEMORY_MANAGEMENT);
677     }
678 
679     return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page);
680 }
681 
682 ULONG
683 NTAPI
684 MmGetPageProtect(PEPROCESS Process, PVOID Address)
685 {
686     PMMPTE PointerPte;
687     ULONG Protect;
688 
689     if (Address >= MmSystemRangeStart)
690     {
691         ASSERT(Process == NULL);
692 
693 #if _MI_PAGING_LEVELS == 2
694         if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
695 #else
696         if (!MiIsPdeForAddressValid(Address))
697 #endif
698         {
699             return PAGE_NOACCESS;
700         }
701     }
702     else
703     {
704         ASSERT(Address < MmSystemRangeStart);
705         ASSERT(Process != NULL);
706 
707         ASSERT(Process == PsGetCurrentProcess());
708 
709         MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
710 
711         if (!MiIsPageTablePresent(Address))
712         {
713             /* It can't be present if there is no PDE */
714             MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
715             return PAGE_NOACCESS;
716         }
717 
718         MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
719     }
720 
721     PointerPte = MiAddressToPte(Address);
722 
723     if (!PointerPte->u.Flush.Valid)
724     {
725         Protect = PAGE_NOACCESS;
726     }
727     else
728     {
729         if (PointerPte->u.Flush.CopyOnWrite)
730             Protect = PAGE_WRITECOPY;
731         else if (PointerPte->u.Flush.Write)
732             Protect = PAGE_READWRITE;
733         else
734             Protect = PAGE_READONLY;
735 #if _MI_PAGING_LEVELS >= 3
736         /* PAE & AMD64 long mode support NoExecute bit */
737         if (!PointerPte->u.Flush.NoExecute)
738             Protect <<= 4;
739 #endif
740         if (PointerPte->u.Flush.CacheDisable)
741             Protect |= PAGE_NOCACHE;
742         if (PointerPte->u.Flush.WriteThrough)
743             Protect |= PAGE_WRITETHROUGH;
744     }
745 
746     if (Address < MmSystemRangeStart)
747         MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
748 
749     return(Protect);
750 }
751 
752 VOID
753 NTAPI
754 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
755 {
756     ULONG ProtectionMask;
757     PMMPTE PointerPte;
758     MMPTE TempPte, OldPte;
759 
760     DPRINT("MmSetPageProtect(Process %p  Address %p  flProtect %x)\n",
761            Process, Address, flProtect);
762 
763     ASSERT(Process != NULL);
764     ASSERT(Address < MmSystemRangeStart);
765 
766     ASSERT(Process == PsGetCurrentProcess());
767 
768     ProtectionMask = MiMakeProtectionMask(flProtect);
769     /* Caller must have checked ! */
770     ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
771 
772     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
773 
774     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
775 
776     PointerPte = MiAddressToPte(Address);
777 
778     /* Sanity check */
779     ASSERT(PointerPte->u.Hard.Owner == 1);
780 
781     TempPte.u.Long = 0;
782     TempPte.u.Hard.PageFrameNumber = PointerPte->u.Hard.PageFrameNumber;
783     TempPte.u.Long |= MmProtectToPteMask[ProtectionMask];
784     TempPte.u.Hard.Owner = 1;
785 
786     /* Only set valid bit if we have to */
787     if ((ProtectionMask != MM_NOACCESS) && !FlagOn(ProtectionMask, MM_GUARDPAGE))
788         TempPte.u.Hard.Valid = 1;
789 
790     /* Keep dirty & accessed bits */
791     TempPte.u.Hard.Accessed = PointerPte->u.Hard.Accessed;
792     TempPte.u.Hard.Dirty = PointerPte->u.Hard.Dirty;
793 
794     OldPte.u.Long = InterlockedExchangePte(PointerPte, TempPte.u.Long);
795 
796     // We should be able to bring a page back from PAGE_NOACCESS
797     if (!OldPte.u.Hard.Valid && (FlagOn(OldPte.u.Long, 0x800) || (OldPte.u.Hard.PageFrameNumber == 0)))
798     {
799         DPRINT1("Invalid Pte %lx\n", OldPte.u.Long);
800         KeBugCheck(MEMORY_MANAGEMENT);
801     }
802 
803     if (OldPte.u.Long != TempPte.u.Long)
804         KeInvalidateTlbEntry(Address);
805 
806     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
807 }
808 
809 VOID
810 NTAPI
811 MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit)
812 {
813     PMMPTE PointerPte;
814 
815     DPRINT("MmSetDirtyBit(Process %p  Address %p  Bit %x)\n",
816            Process, Address, Bit);
817 
818     ASSERT(Process != NULL);
819     ASSERT(Address < MmSystemRangeStart);
820 
821     ASSERT(Process == PsGetCurrentProcess());
822 
823     MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
824 
825     MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
826 
827     PointerPte = MiAddressToPte(Address);
828     // We shouldnl't set dirty bit on non-mapped adresses
829     if (!PointerPte->u.Hard.Valid && (FlagOn(PointerPte->u.Long, 0x800) || (PointerPte->u.Hard.PageFrameNumber == 0)))
830     {
831         DPRINT1("Invalid Pte %lx\n", PointerPte->u.Long);
832         KeBugCheck(MEMORY_MANAGEMENT);
833     }
834 
835     PointerPte->u.Hard.Dirty = !!Bit;
836 
837     if (!Bit)
838         KeInvalidateTlbEntry(Address);
839 
840     MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
841 }
842 
843 CODE_SEG("INIT")
844 VOID
845 NTAPI
846 MmInitGlobalKernelPageDirectory(VOID)
847 {
848     /* Nothing to do here */
849 }
850 
851 #ifdef _M_IX86
852 BOOLEAN
853 Mmi386MakeKernelPageTableGlobal(PVOID Address)
854 {
855     PMMPDE PointerPde = MiAddressToPde(Address);
856     PMMPTE PointerPte = MiAddressToPte(Address);
857 
858     if (PointerPde->u.Hard.Valid == 0)
859     {
860         if (!MiSynchronizeSystemPde(PointerPde))
861             return FALSE;
862         return PointerPte->u.Hard.Valid != 0;
863     }
864     return FALSE;
865 }
866 #endif
867 
868 /* EOF */
869