[NTOSKRNL]
[reactos.git] / reactos / ntoskrnl / mm / i386 / page.c
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 #include "../ARM3/miarm.h"
16
17 #if defined (ALLOC_PRAGMA)
18 #pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
19 #pragma alloc_text(INIT, MiInitPageDirectoryMap)
20 #endif
21
22
23 /* GLOBALS *****************************************************************/
24
25 #define PA_BIT_PRESENT (0)
26 #define PA_BIT_READWRITE (1)
27 #define PA_BIT_USER (2)
28 #define PA_BIT_WT (3)
29 #define PA_BIT_CD (4)
30 #define PA_BIT_ACCESSED (5)
31 #define PA_BIT_DIRTY (6)
32 #define PA_BIT_GLOBAL (8)
33
34 #define PA_PRESENT (1 << PA_BIT_PRESENT)
35 #define PA_READWRITE (1 << PA_BIT_READWRITE)
36 #define PA_USER (1 << PA_BIT_USER)
37 #define PA_DIRTY (1 << PA_BIT_DIRTY)
38 #define PA_WT (1 << PA_BIT_WT)
39 #define PA_CD (1 << PA_BIT_CD)
40 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
41 #define PA_GLOBAL (1 << PA_BIT_GLOBAL)
42
43 #define HYPERSPACE (0xc0400000)
44 #define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPERSPACE && (ULONG)(v) < HYPERSPACE + 0x400000))
45
46 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
47 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
48
49 #if defined(__GNUC__)
50 #define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X)))
51 #else
52 __inline LARGE_INTEGER PTE_TO_PAGE(ULONG npage)
53 {
54 LARGE_INTEGER dummy;
55 dummy.QuadPart = (LONGLONG)(PAGE_MASK(npage));
56 return dummy;
57 }
58 #endif
59
60 const
61 ULONG
62 MmProtectToPteMask[32] =
63 {
64 //
65 // These are the base MM_ protection flags
66 //
67 0,
68 PTE_READONLY | PTE_ENABLE_CACHE,
69 PTE_EXECUTE | PTE_ENABLE_CACHE,
70 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
71 PTE_READWRITE | PTE_ENABLE_CACHE,
72 PTE_WRITECOPY | PTE_ENABLE_CACHE,
73 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
74 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
75 //
76 // These OR in the MM_NOCACHE flag
77 //
78 0,
79 PTE_READONLY | PTE_DISABLE_CACHE,
80 PTE_EXECUTE | PTE_DISABLE_CACHE,
81 PTE_EXECUTE_READ | PTE_DISABLE_CACHE,
82 PTE_READWRITE | PTE_DISABLE_CACHE,
83 PTE_WRITECOPY | PTE_DISABLE_CACHE,
84 PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE,
85 PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE,
86 //
87 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
88 //
89 0,
90 PTE_READONLY | PTE_ENABLE_CACHE,
91 PTE_EXECUTE | PTE_ENABLE_CACHE,
92 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
93 PTE_READWRITE | PTE_ENABLE_CACHE,
94 PTE_WRITECOPY | PTE_ENABLE_CACHE,
95 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
96 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
97 //
98 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
99 //
100 0,
101 PTE_READONLY | PTE_WRITECOMBINED_CACHE,
102 PTE_EXECUTE | PTE_WRITECOMBINED_CACHE,
103 PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE,
104 PTE_READWRITE | PTE_WRITECOMBINED_CACHE,
105 PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
106 PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE,
107 PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
108 };
109
110 const
111 ULONG MmProtectToValue[32] =
112 {
113 PAGE_NOACCESS,
114 PAGE_READONLY,
115 PAGE_EXECUTE,
116 PAGE_EXECUTE_READ,
117 PAGE_READWRITE,
118 PAGE_WRITECOPY,
119 PAGE_EXECUTE_READWRITE,
120 PAGE_EXECUTE_WRITECOPY,
121 PAGE_NOACCESS,
122 PAGE_NOCACHE | PAGE_READONLY,
123 PAGE_NOCACHE | PAGE_EXECUTE,
124 PAGE_NOCACHE | PAGE_EXECUTE_READ,
125 PAGE_NOCACHE | PAGE_READWRITE,
126 PAGE_NOCACHE | PAGE_WRITECOPY,
127 PAGE_NOCACHE | PAGE_EXECUTE_READWRITE,
128 PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY,
129 PAGE_NOACCESS,
130 PAGE_GUARD | PAGE_READONLY,
131 PAGE_GUARD | PAGE_EXECUTE,
132 PAGE_GUARD | PAGE_EXECUTE_READ,
133 PAGE_GUARD | PAGE_READWRITE,
134 PAGE_GUARD | PAGE_WRITECOPY,
135 PAGE_GUARD | PAGE_EXECUTE_READWRITE,
136 PAGE_GUARD | PAGE_EXECUTE_WRITECOPY,
137 PAGE_NOACCESS,
138 PAGE_WRITECOMBINE | PAGE_READONLY,
139 PAGE_WRITECOMBINE | PAGE_EXECUTE,
140 PAGE_WRITECOMBINE | PAGE_EXECUTE_READ,
141 PAGE_WRITECOMBINE | PAGE_READWRITE,
142 PAGE_WRITECOMBINE | PAGE_WRITECOPY,
143 PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE,
144 PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY
145 };
146
147 /* FUNCTIONS ***************************************************************/
148
149 BOOLEAN MmUnmapPageTable(PULONG Pt);
150
151 VOID
152 MiFlushTlb(PULONG Pt, PVOID Address)
153 {
154 if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart)
155 {
156 KeInvalidateTlbEntry(Address);
157 }
158 }
159
160 static ULONG
161 ProtectToPTE(ULONG flProtect)
162 {
163 ULONG Attributes = 0;
164
165 if (flProtect & (PAGE_NOACCESS|PAGE_GUARD))
166 {
167 Attributes = 0;
168 }
169 else if (flProtect & PAGE_IS_WRITABLE)
170 {
171 Attributes = PA_PRESENT | PA_READWRITE;
172 }
173 else if (flProtect & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE))
174 {
175 Attributes = PA_PRESENT;
176 }
177 else
178 {
179 DPRINT1("Unknown main protection type.\n");
180 KeBugCheck(MEMORY_MANAGEMENT);
181 }
182
183 if (flProtect & PAGE_SYSTEM)
184 {
185 }
186 else
187 {
188 Attributes = Attributes | PA_USER;
189 }
190 if (flProtect & PAGE_NOCACHE)
191 {
192 Attributes = Attributes | PA_CD;
193 }
194 if (flProtect & PAGE_WRITETHROUGH)
195 {
196 Attributes = Attributes | PA_WT;
197 }
198 return(Attributes);
199 }
200
201 /* Taken from ARM3/pagfault.c */
202 BOOLEAN
203 FORCEINLINE
204 MiSynchronizeSystemPde(PMMPDE PointerPde)
205 {
206 MMPDE SystemPde;
207 ULONG Index;
208
209 /* Get the Index from the PDE */
210 Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
211
212 /* Copy the PDE from the double-mapped system page directory */
213 SystemPde = MmSystemPagePtes[Index];
214 *PointerPde = SystemPde;
215
216 /* Make sure we re-read the PDE and PTE */
217 KeMemoryBarrierWithoutFence();
218
219 /* Return, if we had success */
220 return SystemPde.u.Hard.Valid != 0;
221 }
222
223 NTSTATUS
224 NTAPI
225 MiDispatchFault(IN BOOLEAN StoreInstruction,
226 IN PVOID Address,
227 IN PMMPTE PointerPte,
228 IN PMMPTE PointerProtoPte,
229 IN BOOLEAN Recursive,
230 IN PEPROCESS Process,
231 IN PVOID TrapInformation,
232 IN PVOID Vad);
233
234 NTSTATUS
235 NTAPI
236 MiFillSystemPageDirectory(IN PVOID Base,
237 IN SIZE_T NumberOfBytes);
238
239 static PULONG
240 MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create)
241 {
242 PFN_NUMBER Pfn;
243 PULONG Pt;
244 PMMPDE PointerPde;
245
246 if (Address < MmSystemRangeStart)
247 {
248 /* We should have a process for user land addresses */
249 ASSERT(Process != NULL);
250
251 if(Process != PsGetCurrentProcess())
252 {
253 PMMPDE PdeBase;
254 ULONG PdeOffset = MiGetPdeOffset(Address);
255
256 /* Nobody but page fault should ask for creating the PDE,
257 * Which imples that Process is the current one */
258 ASSERT(Create == FALSE);
259
260 PdeBase = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
261 if (PdeBase == NULL)
262 {
263 KeBugCheck(MEMORY_MANAGEMENT);
264 }
265 PointerPde = PdeBase + PdeOffset;
266 if (PointerPde->u.Hard.Valid == 0)
267 {
268 MmDeleteHyperspaceMapping(PdeBase);
269 return NULL;
270 }
271 else
272 {
273 Pfn = PointerPde->u.Hard.PageFrameNumber;
274 }
275 MmDeleteHyperspaceMapping(PdeBase);
276 Pt = MmCreateHyperspaceMapping(Pfn);
277 if (Pt == NULL)
278 {
279 KeBugCheck(MEMORY_MANAGEMENT);
280 }
281 return Pt + MiAddressToPteOffset(Address);
282 }
283 /* This is for our process */
284 PointerPde = MiAddressToPde(Address);
285 Pt = (PULONG)MiAddressToPte(Address);
286 if (PointerPde->u.Hard.Valid == 0)
287 {
288 NTSTATUS Status;
289 if (Create == FALSE)
290 {
291 return NULL;
292 }
293 ASSERT(PointerPde->u.Long == 0);
294
295 MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
296 Status = MiDispatchFault(TRUE,
297 Pt,
298 PointerPde,
299 NULL,
300 FALSE,
301 PsGetCurrentProcess(),
302 NULL,
303 NULL);
304 DBG_UNREFERENCED_LOCAL_VARIABLE(Status);
305 ASSERT(KeAreAllApcsDisabled() == TRUE);
306 ASSERT(PointerPde->u.Hard.Valid == 1);
307 }
308 return (PULONG)MiAddressToPte(Address);
309 }
310
311 /* This is for kernel land address */
312 ASSERT(Process == NULL);
313 PointerPde = MiAddressToPde(Address);
314 Pt = (PULONG)MiAddressToPte(Address);
315 if (PointerPde->u.Hard.Valid == 0)
316 {
317 /* Let ARM3 synchronize the PDE */
318 if(!MiSynchronizeSystemPde(PointerPde))
319 {
320 /* PDE (still) not valid, let ARM3 allocate one if asked */
321 if(Create == FALSE)
322 return NULL;
323 MiFillSystemPageDirectory(Address, PAGE_SIZE);
324 }
325 }
326 return Pt;
327 }
328
329 BOOLEAN MmUnmapPageTable(PULONG Pt)
330 {
331 if (!IS_HYPERSPACE(Pt))
332 {
333 return TRUE;
334 }
335
336 if (Pt)
337 {
338 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
339 }
340 return FALSE;
341 }
342
343 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
344 {
345 ULONG Pte;
346 PULONG Pt;
347
348 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
349 if (Pt)
350 {
351 Pte = *Pt;
352 MmUnmapPageTable(Pt);
353 return Pte;
354 }
355 return 0;
356 }
357
358 PFN_NUMBER
359 NTAPI
360 MmGetPfnForProcess(PEPROCESS Process,
361 PVOID Address)
362 {
363 ULONG Entry;
364 Entry = MmGetPageEntryForProcess(Process, Address);
365 if (!(Entry & PA_PRESENT))
366 {
367 return 0;
368 }
369 return(PTE_TO_PFN(Entry));
370 }
371
372 VOID
373 NTAPI
374 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_NUMBER Page)
375 /*
376 * FUNCTION: Delete a virtual mapping
377 */
378 {
379 BOOLEAN WasValid;
380 ULONG Pte;
381 PULONG Pt;
382
383 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
384 if (Pt == NULL)
385 {
386 KeBugCheck(MEMORY_MANAGEMENT);
387 }
388
389 /*
390 * Atomically disable the present bit and get the old value.
391 */
392 do
393 {
394 Pte = *Pt;
395 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_PRESENT, Pte));
396
397 MiFlushTlb(Pt, Address);
398
399 WasValid = (Pte & PA_PRESENT);
400 if (!WasValid)
401 {
402 KeBugCheck(MEMORY_MANAGEMENT);
403 }
404
405 /*
406 * Return some information to the caller
407 */
408 if (WasDirty != NULL)
409 {
410 *WasDirty = Pte & PA_DIRTY;
411 }
412 if (Page != NULL)
413 {
414 *Page = PTE_TO_PFN(Pte);
415 }
416 }
417
418 VOID
419 NTAPI
420 MmRawDeleteVirtualMapping(PVOID Address)
421 {
422 PULONG Pt;
423
424 Pt = MmGetPageTableForProcess(NULL, Address, FALSE);
425 if (Pt && *Pt)
426 {
427 /*
428 * Set the entry to zero
429 */
430 InterlockedExchangePte(Pt, 0);
431 MiFlushTlb(Pt, Address);
432 }
433 }
434
435 VOID
436 NTAPI
437 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,
438 BOOLEAN* WasDirty, PPFN_NUMBER Page)
439 /*
440 * FUNCTION: Delete a virtual mapping
441 */
442 {
443 BOOLEAN WasValid = FALSE;
444 PFN_NUMBER Pfn;
445 ULONG Pte;
446 PULONG Pt;
447
448 DPRINT("MmDeleteVirtualMapping(%p, %p, %u, %p, %p)\n",
449 Process, Address, FreePage, WasDirty, Page);
450
451 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
452
453 if (Pt == NULL)
454 {
455 if (WasDirty != NULL)
456 {
457 *WasDirty = FALSE;
458 }
459 if (Page != NULL)
460 {
461 *Page = 0;
462 }
463 return;
464 }
465
466 /*
467 * Atomically set the entry to zero and get the old value.
468 */
469 Pte = InterlockedExchangePte(Pt, 0);
470
471 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
472 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
473 WasValid = (Pte & PA_PRESENT) || ((Pte >> PAGE_SHIFT) && !(Pte & 0x800));
474 if (WasValid)
475 {
476 /* Flush the TLB since we transitioned this PTE
477 * from valid to invalid so any stale translations
478 * are removed from the cache */
479 MiFlushTlb(Pt, Address);
480
481 if (Address < MmSystemRangeStart)
482 {
483 /* Remove PDE reference */
484 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
485 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
486 }
487
488 Pfn = PTE_TO_PFN(Pte);
489
490 if (FreePage)
491 {
492 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
493 Pfn = 0;
494 }
495 }
496 else
497 {
498 MmUnmapPageTable(Pt);
499 Pfn = 0;
500 }
501
502 /*
503 * Return some information to the caller
504 */
505 if (WasDirty != NULL)
506 {
507 *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE;
508 }
509 if (Page != NULL)
510 {
511 *Page = Pfn;
512 }
513 }
514
515 VOID
516 NTAPI
517 MmGetPageFileMapping(PEPROCESS Process, PVOID Address,
518 SWAPENTRY* SwapEntry)
519 /*
520 * FUNCTION: Get a page file mapping
521 */
522 {
523 ULONG Entry = MmGetPageEntryForProcess(Process, Address);
524 *SwapEntry = Entry >> 1;
525 }
526
527 VOID
528 NTAPI
529 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
530 SWAPENTRY* SwapEntry)
531 /*
532 * FUNCTION: Delete a virtual mapping
533 */
534 {
535 ULONG Pte;
536 PULONG Pt;
537
538 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
539
540 if (Pt == NULL)
541 {
542 *SwapEntry = 0;
543 return;
544 }
545
546 /*
547 * Atomically set the entry to zero and get the old value.
548 */
549 Pte = InterlockedExchangePte(Pt, 0);
550
551 if (Address < MmSystemRangeStart)
552 {
553 /* Remove PDE reference */
554 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
555 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
556 }
557
558 /* We don't need to flush here because page file entries
559 * are invalid translations, so the processor won't cache them */
560 MmUnmapPageTable(Pt);
561
562 if ((Pte & PA_PRESENT) || !(Pte & 0x800))
563 {
564 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte);
565 KeBugCheck(MEMORY_MANAGEMENT);
566 }
567
568 /*
569 * Return some information to the caller
570 */
571 *SwapEntry = Pte >> 1;
572 }
573
574 BOOLEAN
575 Mmi386MakeKernelPageTableGlobal(PVOID Address)
576 {
577 PMMPDE PointerPde = MiAddressToPde(Address);
578 PMMPTE PointerPte = MiAddressToPte(Address);
579
580 if (PointerPde->u.Hard.Valid == 0)
581 {
582 if(!MiSynchronizeSystemPde(PointerPde))
583 return FALSE;
584 return PointerPte->u.Hard.Valid != 0;
585 }
586 return FALSE;
587 }
588
589 BOOLEAN
590 NTAPI
591 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
592 {
593 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
594 }
595
596 VOID
597 NTAPI
598 MmSetCleanPage(PEPROCESS Process, PVOID Address)
599 {
600 PULONG Pt;
601 ULONG Pte;
602
603 if (Address < MmSystemRangeStart && Process == NULL)
604 {
605 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
606 KeBugCheck(MEMORY_MANAGEMENT);
607 }
608
609 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
610 if (Pt == NULL)
611 {
612 KeBugCheck(MEMORY_MANAGEMENT);
613 }
614
615 do
616 {
617 Pte = *Pt;
618 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
619
620 if (!(Pte & PA_PRESENT))
621 {
622 KeBugCheck(MEMORY_MANAGEMENT);
623 }
624 else if (Pte & PA_DIRTY)
625 {
626 MiFlushTlb(Pt, Address);
627 }
628 else
629 {
630 MmUnmapPageTable(Pt);
631 }
632 }
633
634 VOID
635 NTAPI
636 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
637 {
638 PULONG Pt;
639 ULONG Pte;
640
641 if (Address < MmSystemRangeStart && Process == NULL)
642 {
643 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
644 KeBugCheck(MEMORY_MANAGEMENT);
645 }
646
647 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
648 if (Pt == NULL)
649 {
650 KeBugCheck(MEMORY_MANAGEMENT);
651 }
652
653 do
654 {
655 Pte = *Pt;
656 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
657
658 if (!(Pte & PA_PRESENT))
659 {
660 KeBugCheck(MEMORY_MANAGEMENT);
661 }
662 else
663 {
664 /* The processor will never clear this bit itself, therefore
665 * we do not need to flush the TLB here when setting it */
666 MmUnmapPageTable(Pt);
667 }
668 }
669
670 VOID
671 NTAPI
672 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
673 {
674 PULONG Pt;
675 ULONG Pte;
676
677 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
678 if (Pt == NULL)
679 {
680 //HACK to get DPH working, waiting for MM rewrite :-/
681 //KeBugCheck(MEMORY_MANAGEMENT);
682 return;
683 }
684
685 /* Do not mark a 0 page as present */
686 if(0 == InterlockedCompareExchangePte(Pt, 0, 0))
687 return;
688
689 do
690 {
691 Pte = *Pt;
692 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
693
694 /* We don't need to flush the TLB here because it
695 * won't cache translations for non-present pages */
696 MmUnmapPageTable(Pt);
697 }
698
699 BOOLEAN
700 NTAPI
701 MmIsPagePresent(PEPROCESS Process, PVOID Address)
702 {
703 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
704 }
705
706 BOOLEAN
707 NTAPI
708 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
709 {
710 ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address);
711 return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT);
712 }
713
714 BOOLEAN
715 NTAPI
716 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
717 {
718 ULONG Entry;
719 Entry = MmGetPageEntryForProcess(Process, Address);
720 return !(Entry & PA_PRESENT) && (Entry & 0x800);
721 }
722
723 NTSTATUS
724 NTAPI
725 MmCreatePageFileMapping(PEPROCESS Process,
726 PVOID Address,
727 SWAPENTRY SwapEntry)
728 {
729 PULONG Pt;
730 ULONG Pte;
731
732 if (Process == NULL && Address < MmSystemRangeStart)
733 {
734 DPRINT1("No process\n");
735 KeBugCheck(MEMORY_MANAGEMENT);
736 }
737 if (Process != NULL && Address >= MmSystemRangeStart)
738 {
739 DPRINT1("Setting kernel address with process context\n");
740 KeBugCheck(MEMORY_MANAGEMENT);
741 }
742
743 if (SwapEntry & (1 << 31))
744 {
745 KeBugCheck(MEMORY_MANAGEMENT);
746 }
747
748 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
749 if (Pt == NULL)
750 {
751 /* Nobody should page out an address that hasn't even been mapped */
752 /* But we might place a wait entry first, requiring the page table */
753 if (SwapEntry != MM_WAIT_ENTRY)
754 {
755 KeBugCheck(MEMORY_MANAGEMENT);
756 }
757 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
758 }
759 Pte = InterlockedExchangePte(Pt, SwapEntry << 1);
760 if (Pte != 0)
761 {
762 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
763 }
764
765 if (Address < MmSystemRangeStart)
766 {
767 /* Add PDE reference */
768 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
769 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
770 }
771
772 /* We don't need to flush the TLB here because it
773 * only caches valid translations and a zero PTE
774 * is not a valid translation */
775 MmUnmapPageTable(Pt);
776
777 return(STATUS_SUCCESS);
778 }
779
780
781 NTSTATUS
782 NTAPI
783 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
784 PVOID Address,
785 ULONG flProtect,
786 PPFN_NUMBER Pages,
787 ULONG PageCount)
788 {
789 ULONG Attributes;
790 PVOID Addr;
791 ULONG i;
792 ULONG oldPdeOffset, PdeOffset;
793 PULONG Pt = NULL;
794 ULONG Pte;
795 DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
796 Process, Address, flProtect, Pages, *Pages, PageCount);
797
798 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
799
800 if (Process == NULL)
801 {
802 if (Address < MmSystemRangeStart)
803 {
804 DPRINT1("No process\n");
805 KeBugCheck(MEMORY_MANAGEMENT);
806 }
807 if (PageCount > 0x10000 ||
808 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
809 {
810 DPRINT1("Page count too large\n");
811 KeBugCheck(MEMORY_MANAGEMENT);
812 }
813 }
814 else
815 {
816 if (Address >= MmSystemRangeStart)
817 {
818 DPRINT1("Setting kernel address with process context\n");
819 KeBugCheck(MEMORY_MANAGEMENT);
820 }
821 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
822 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
823 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
824 {
825 DPRINT1("Page Count too large\n");
826 KeBugCheck(MEMORY_MANAGEMENT);
827 }
828 }
829
830 Attributes = ProtectToPTE(flProtect);
831 Attributes &= 0xfff;
832 if (Address >= MmSystemRangeStart)
833 {
834 Attributes &= ~PA_USER;
835 }
836 else
837 {
838 Attributes |= PA_USER;
839 }
840
841 Addr = Address;
842 /* MmGetPageTableForProcess should be called on the first run, so
843 * let this trigger it */
844 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
845 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
846 {
847 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
848 {
849 DPRINT1("Setting physical address but not allowing access at address "
850 "0x%p with attributes %x/%x.\n",
851 Addr, Attributes, flProtect);
852 KeBugCheck(MEMORY_MANAGEMENT);
853 }
854 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
855 if (oldPdeOffset != PdeOffset)
856 {
857 if(Pt) MmUnmapPageTable(Pt);
858 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
859 if (Pt == NULL)
860 {
861 KeBugCheck(MEMORY_MANAGEMENT);
862 }
863 }
864 else
865 {
866 Pt++;
867 }
868 oldPdeOffset = PdeOffset;
869
870 Pte = InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
871
872 /* There should not be anything valid here */
873 if (Pte != 0)
874 {
875 DPRINT1("Bad PTE %lx\n", Pte);
876 KeBugCheck(MEMORY_MANAGEMENT);
877 }
878
879 /* We don't need to flush the TLB here because it only caches valid translations
880 * and we're moving this PTE from invalid to valid so it can't be cached right now */
881
882 if (Addr < MmSystemRangeStart)
883 {
884 /* Add PDE reference */
885 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++;
886 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_COUNT);
887 }
888 }
889
890 ASSERT(Addr > Address);
891 MmUnmapPageTable(Pt);
892
893 return(STATUS_SUCCESS);
894 }
895
896 NTSTATUS
897 NTAPI
898 MmCreateVirtualMapping(PEPROCESS Process,
899 PVOID Address,
900 ULONG flProtect,
901 PPFN_NUMBER Pages,
902 ULONG PageCount)
903 {
904 ULONG i;
905
906 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
907 for (i = 0; i < PageCount; i++)
908 {
909 if (!MmIsPageInUse(Pages[i]))
910 {
911 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
912 KeBugCheck(MEMORY_MANAGEMENT);
913 }
914 }
915
916 return(MmCreateVirtualMappingUnsafe(Process,
917 Address,
918 flProtect,
919 Pages,
920 PageCount));
921 }
922
923 ULONG
924 NTAPI
925 MmGetPageProtect(PEPROCESS Process, PVOID Address)
926 {
927 ULONG Entry;
928 ULONG Protect;
929
930 Entry = MmGetPageEntryForProcess(Process, Address);
931
932
933 if (!(Entry & PA_PRESENT))
934 {
935 Protect = PAGE_NOACCESS;
936 }
937 else
938 {
939 if (Entry & PA_READWRITE)
940 {
941 Protect = PAGE_READWRITE;
942 }
943 else
944 {
945 Protect = PAGE_EXECUTE_READ;
946 }
947 if (Entry & PA_CD)
948 {
949 Protect |= PAGE_NOCACHE;
950 }
951 if (Entry & PA_WT)
952 {
953 Protect |= PAGE_WRITETHROUGH;
954 }
955 if (!(Entry & PA_USER))
956 {
957 Protect |= PAGE_SYSTEM;
958 }
959
960 }
961 return(Protect);
962 }
963
964 VOID
965 NTAPI
966 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
967 {
968 ULONG Attributes = 0;
969 PULONG Pt;
970 ULONG Pte;
971
972 DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
973 Process, Address, flProtect);
974
975 Attributes = ProtectToPTE(flProtect);
976
977 Attributes &= 0xfff;
978 if (Address >= MmSystemRangeStart)
979 {
980 Attributes &= ~PA_USER;
981 }
982 else
983 {
984 Attributes |= PA_USER;
985 }
986
987 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
988 if (Pt == NULL)
989 {
990 KeBugCheck(MEMORY_MANAGEMENT);
991 }
992 Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
993
994 // We should be able to bring a page back from PAGE_NOACCESS
995 if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT))
996 {
997 DPRINT1("Invalid Pte %lx\n", Pte);
998 KeBugCheck(MEMORY_MANAGEMENT);
999 }
1000
1001 if((Pte & Attributes) != Attributes)
1002 MiFlushTlb(Pt, Address);
1003 else
1004 MmUnmapPageTable(Pt);
1005 }
1006
1007 VOID
1008 INIT_FUNCTION
1009 NTAPI
1010 MmInitGlobalKernelPageDirectory(VOID)
1011 {
1012 /* Nothing to do here */
1013 }
1014
1015 /* EOF */