Synchronize with trunk r58528.
[reactos.git] / 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 ASSERT(KeAreAllApcsDisabled() == TRUE);
305 ASSERT(PointerPde->u.Hard.Valid == 1);
306 }
307 return (PULONG)MiAddressToPte(Address);
308 }
309
310 /* This is for kernel land address */
311 ASSERT(Process == NULL);
312 PointerPde = MiAddressToPde(Address);
313 Pt = (PULONG)MiAddressToPte(Address);
314 if (PointerPde->u.Hard.Valid == 0)
315 {
316 /* Let ARM3 synchronize the PDE */
317 if(!MiSynchronizeSystemPde(PointerPde))
318 {
319 /* PDE (still) not valid, let ARM3 allocate one if asked */
320 if(Create == FALSE)
321 return NULL;
322 MiFillSystemPageDirectory(Address, PAGE_SIZE);
323 }
324 }
325 return Pt;
326 }
327
328 BOOLEAN MmUnmapPageTable(PULONG Pt)
329 {
330 if (!IS_HYPERSPACE(Pt))
331 {
332 return TRUE;
333 }
334
335 if (Pt)
336 {
337 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
338 }
339 return FALSE;
340 }
341
342 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
343 {
344 ULONG Pte;
345 PULONG Pt;
346
347 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
348 if (Pt)
349 {
350 Pte = *Pt;
351 MmUnmapPageTable(Pt);
352 return Pte;
353 }
354 return 0;
355 }
356
357 PFN_NUMBER
358 NTAPI
359 MmGetPfnForProcess(PEPROCESS Process,
360 PVOID Address)
361 {
362 ULONG Entry;
363 Entry = MmGetPageEntryForProcess(Process, Address);
364 if (!(Entry & PA_PRESENT))
365 {
366 return 0;
367 }
368 return(PTE_TO_PFN(Entry));
369 }
370
371 VOID
372 NTAPI
373 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_NUMBER Page)
374 /*
375 * FUNCTION: Delete a virtual mapping
376 */
377 {
378 BOOLEAN WasValid;
379 ULONG Pte;
380 PULONG Pt;
381
382 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
383 if (Pt == NULL)
384 {
385 KeBugCheck(MEMORY_MANAGEMENT);
386 }
387
388 /*
389 * Atomically disable the present bit and get the old value.
390 */
391 do
392 {
393 Pte = *Pt;
394 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_PRESENT, Pte));
395
396 MiFlushTlb(Pt, Address);
397
398 WasValid = (Pte & PA_PRESENT);
399 if (!WasValid)
400 {
401 KeBugCheck(MEMORY_MANAGEMENT);
402 }
403
404 /*
405 * Return some information to the caller
406 */
407 if (WasDirty != NULL)
408 {
409 *WasDirty = Pte & PA_DIRTY;
410 }
411 if (Page != NULL)
412 {
413 *Page = PTE_TO_PFN(Pte);
414 }
415 }
416
417 VOID
418 NTAPI
419 MmRawDeleteVirtualMapping(PVOID Address)
420 {
421 PULONG Pt;
422
423 Pt = MmGetPageTableForProcess(NULL, Address, FALSE);
424 if (Pt && *Pt)
425 {
426 /*
427 * Set the entry to zero
428 */
429 InterlockedExchangePte(Pt, 0);
430 MiFlushTlb(Pt, Address);
431 }
432 }
433
434 VOID
435 NTAPI
436 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,
437 BOOLEAN* WasDirty, PPFN_NUMBER Page)
438 /*
439 * FUNCTION: Delete a virtual mapping
440 */
441 {
442 BOOLEAN WasValid = FALSE;
443 PFN_NUMBER Pfn;
444 ULONG Pte;
445 PULONG Pt;
446
447 DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
448 Process, Address, FreePage, WasDirty, Page);
449
450 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
451
452 if (Pt == NULL)
453 {
454 if (WasDirty != NULL)
455 {
456 *WasDirty = FALSE;
457 }
458 if (Page != NULL)
459 {
460 *Page = 0;
461 }
462 return;
463 }
464
465 /*
466 * Atomically set the entry to zero and get the old value.
467 */
468 Pte = InterlockedExchangePte(Pt, 0);
469
470 /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
471 * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
472 WasValid = (Pte & PA_PRESENT) || ((Pte >> PAGE_SHIFT) && !(Pte & 0x800));
473 if (WasValid)
474 {
475 /* Flush the TLB since we transitioned this PTE
476 * from valid to invalid so any stale translations
477 * are removed from the cache */
478 MiFlushTlb(Pt, Address);
479
480 if (Address < MmSystemRangeStart)
481 {
482 /* Remove PDE reference */
483 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
484 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
485 }
486
487 Pfn = PTE_TO_PFN(Pte);
488
489 if (FreePage)
490 {
491 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
492 Pfn = 0;
493 }
494 }
495 else
496 {
497 MmUnmapPageTable(Pt);
498 Pfn = 0;
499 }
500
501 /*
502 * Return some information to the caller
503 */
504 if (WasDirty != NULL)
505 {
506 *WasDirty = ((Pte & PA_DIRTY) && (Pte & PA_PRESENT)) ? TRUE : FALSE;
507 }
508 if (Page != NULL)
509 {
510 *Page = Pfn;
511 }
512 }
513
514 VOID
515 NTAPI
516 MmGetPageFileMapping(PEPROCESS Process, PVOID Address,
517 SWAPENTRY* SwapEntry)
518 /*
519 * FUNCTION: Get a page file mapping
520 */
521 {
522 ULONG Entry = MmGetPageEntryForProcess(Process, Address);
523 *SwapEntry = Entry >> 1;
524 }
525
526 VOID
527 NTAPI
528 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
529 SWAPENTRY* SwapEntry)
530 /*
531 * FUNCTION: Delete a virtual mapping
532 */
533 {
534 ULONG Pte;
535 PULONG Pt;
536
537 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
538
539 if (Pt == NULL)
540 {
541 *SwapEntry = 0;
542 return;
543 }
544
545 /*
546 * Atomically set the entry to zero and get the old value.
547 */
548 Pte = InterlockedExchangePte(Pt, 0);
549
550 if (Address < MmSystemRangeStart)
551 {
552 /* Remove PDE reference */
553 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
554 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
555 }
556
557 /* We don't need to flush here because page file entries
558 * are invalid translations, so the processor won't cache them */
559 MmUnmapPageTable(Pt);
560
561 if ((Pte & PA_PRESENT) || !(Pte & 0x800))
562 {
563 DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte);
564 KeBugCheck(MEMORY_MANAGEMENT);
565 }
566
567 /*
568 * Return some information to the caller
569 */
570 *SwapEntry = Pte >> 1;
571 }
572
573 BOOLEAN
574 Mmi386MakeKernelPageTableGlobal(PVOID Address)
575 {
576 PMMPDE PointerPde = MiAddressToPde(Address);
577 PMMPTE PointerPte = MiAddressToPte(Address);
578
579 if (PointerPde->u.Hard.Valid == 0)
580 {
581 if(!MiSynchronizeSystemPde(PointerPde))
582 return FALSE;
583 return PointerPte->u.Hard.Valid != 0;
584 }
585 return FALSE;
586 }
587
588 BOOLEAN
589 NTAPI
590 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
591 {
592 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
593 }
594
595 VOID
596 NTAPI
597 MmSetCleanPage(PEPROCESS Process, PVOID Address)
598 {
599 PULONG Pt;
600 ULONG Pte;
601
602 if (Address < MmSystemRangeStart && Process == NULL)
603 {
604 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
605 KeBugCheck(MEMORY_MANAGEMENT);
606 }
607
608 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
609 if (Pt == NULL)
610 {
611 KeBugCheck(MEMORY_MANAGEMENT);
612 }
613
614 do
615 {
616 Pte = *Pt;
617 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
618
619 if (!(Pte & PA_PRESENT))
620 {
621 KeBugCheck(MEMORY_MANAGEMENT);
622 }
623 else if (Pte & PA_DIRTY)
624 {
625 MiFlushTlb(Pt, Address);
626 }
627 else
628 {
629 MmUnmapPageTable(Pt);
630 }
631 }
632
633 VOID
634 NTAPI
635 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
636 {
637 PULONG Pt;
638 ULONG Pte;
639
640 if (Address < MmSystemRangeStart && Process == NULL)
641 {
642 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
643 KeBugCheck(MEMORY_MANAGEMENT);
644 }
645
646 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
647 if (Pt == NULL)
648 {
649 KeBugCheck(MEMORY_MANAGEMENT);
650 }
651
652 do
653 {
654 Pte = *Pt;
655 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
656
657 if (!(Pte & PA_PRESENT))
658 {
659 KeBugCheck(MEMORY_MANAGEMENT);
660 }
661 else
662 {
663 /* The processor will never clear this bit itself, therefore
664 * we do not need to flush the TLB here when setting it */
665 MmUnmapPageTable(Pt);
666 }
667 }
668
669 VOID
670 NTAPI
671 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
672 {
673 PULONG Pt;
674 ULONG Pte;
675
676 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
677 if (Pt == NULL)
678 {
679 //HACK to get DPH working, waiting for MM rewrite :-/
680 //KeBugCheck(MEMORY_MANAGEMENT);
681 return;
682 }
683
684 /* Do not mark a 0 page as present */
685 if(0 == InterlockedCompareExchangePte(Pt, 0, 0))
686 return;
687
688 do
689 {
690 Pte = *Pt;
691 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
692
693 /* We don't need to flush the TLB here because it
694 * won't cache translations for non-present pages */
695 MmUnmapPageTable(Pt);
696 }
697
698 BOOLEAN
699 NTAPI
700 MmIsPagePresent(PEPROCESS Process, PVOID Address)
701 {
702 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
703 }
704
705 BOOLEAN
706 NTAPI
707 MmIsDisabledPage(PEPROCESS Process, PVOID Address)
708 {
709 ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address);
710 return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT);
711 }
712
713 BOOLEAN
714 NTAPI
715 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
716 {
717 ULONG Entry;
718 Entry = MmGetPageEntryForProcess(Process, Address);
719 return !(Entry & PA_PRESENT) && (Entry & 0x800);
720 }
721
722 NTSTATUS
723 NTAPI
724 MmCreatePageFileMapping(PEPROCESS Process,
725 PVOID Address,
726 SWAPENTRY SwapEntry)
727 {
728 PULONG Pt;
729 ULONG Pte;
730
731 if (Process == NULL && Address < MmSystemRangeStart)
732 {
733 DPRINT1("No process\n");
734 KeBugCheck(MEMORY_MANAGEMENT);
735 }
736 if (Process != NULL && Address >= MmSystemRangeStart)
737 {
738 DPRINT1("Setting kernel address with process context\n");
739 KeBugCheck(MEMORY_MANAGEMENT);
740 }
741
742 if (SwapEntry & (1 << 31))
743 {
744 KeBugCheck(MEMORY_MANAGEMENT);
745 }
746
747 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
748 if (Pt == NULL)
749 {
750 /* Nobody should page out an address that hasn't even been mapped */
751 /* But we might place a wait entry first, requiring the page table */
752 if (SwapEntry != MM_WAIT_ENTRY)
753 {
754 KeBugCheck(MEMORY_MANAGEMENT);
755 }
756 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
757 }
758 Pte = InterlockedExchangePte(Pt, SwapEntry << 1);
759 if (Pte != 0)
760 {
761 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
762 }
763
764 if (Address < MmSystemRangeStart)
765 {
766 /* Add PDE reference */
767 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
768 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
769 }
770
771 /* We don't need to flush the TLB here because it
772 * only caches valid translations and a zero PTE
773 * is not a valid translation */
774 MmUnmapPageTable(Pt);
775
776 return(STATUS_SUCCESS);
777 }
778
779
780 NTSTATUS
781 NTAPI
782 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
783 PVOID Address,
784 ULONG flProtect,
785 PPFN_NUMBER Pages,
786 ULONG PageCount)
787 {
788 ULONG Attributes;
789 PVOID Addr;
790 ULONG i;
791 ULONG oldPdeOffset, PdeOffset;
792 PULONG Pt = NULL;
793 ULONG Pte;
794 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
795 Process, Address, flProtect, Pages, *Pages, PageCount);
796
797 ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
798
799 if (Process == NULL)
800 {
801 if (Address < MmSystemRangeStart)
802 {
803 DPRINT1("No process\n");
804 KeBugCheck(MEMORY_MANAGEMENT);
805 }
806 if (PageCount > 0x10000 ||
807 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
808 {
809 DPRINT1("Page count too large\n");
810 KeBugCheck(MEMORY_MANAGEMENT);
811 }
812 }
813 else
814 {
815 if (Address >= MmSystemRangeStart)
816 {
817 DPRINT1("Setting kernel address with process context\n");
818 KeBugCheck(MEMORY_MANAGEMENT);
819 }
820 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
821 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
822 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
823 {
824 DPRINT1("Page Count too large\n");
825 KeBugCheck(MEMORY_MANAGEMENT);
826 }
827 }
828
829 Attributes = ProtectToPTE(flProtect);
830 Attributes &= 0xfff;
831 if (Address >= MmSystemRangeStart)
832 {
833 Attributes &= ~PA_USER;
834 }
835 else
836 {
837 Attributes |= PA_USER;
838 }
839
840 Addr = Address;
841 /* MmGetPageTableForProcess should be called on the first run, so
842 * let this trigger it */
843 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
844 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
845 {
846 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
847 {
848 DPRINT1("Setting physical address but not allowing access at address "
849 "0x%.8X with attributes %x/%x.\n",
850 Addr, Attributes, flProtect);
851 KeBugCheck(MEMORY_MANAGEMENT);
852 }
853 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
854 if (oldPdeOffset != PdeOffset)
855 {
856 if(Pt) MmUnmapPageTable(Pt);
857 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
858 if (Pt == NULL)
859 {
860 KeBugCheck(MEMORY_MANAGEMENT);
861 }
862 }
863 else
864 {
865 Pt++;
866 }
867 oldPdeOffset = PdeOffset;
868
869 Pte = InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
870
871 /* There should not be anything valid here */
872 if (Pte != 0)
873 {
874 DPRINT1("Bad PTE %lx\n", Pte);
875 KeBugCheck(MEMORY_MANAGEMENT);
876 }
877
878 /* We don't need to flush the TLB here because it only caches valid translations
879 * and we're moving this PTE from invalid to valid so it can't be cached right now */
880
881 if (Addr < MmSystemRangeStart)
882 {
883 /* Add PDE reference */
884 Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++;
885 ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_COUNT);
886 }
887 }
888
889 ASSERT(Addr > Address);
890 MmUnmapPageTable(Pt);
891
892 return(STATUS_SUCCESS);
893 }
894
895 NTSTATUS
896 NTAPI
897 MmCreateVirtualMapping(PEPROCESS Process,
898 PVOID Address,
899 ULONG flProtect,
900 PPFN_NUMBER Pages,
901 ULONG PageCount)
902 {
903 ULONG i;
904
905 ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
906 for (i = 0; i < PageCount; i++)
907 {
908 if (!MmIsPageInUse(Pages[i]))
909 {
910 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
911 KeBugCheck(MEMORY_MANAGEMENT);
912 }
913 }
914
915 return(MmCreateVirtualMappingUnsafe(Process,
916 Address,
917 flProtect,
918 Pages,
919 PageCount));
920 }
921
922 ULONG
923 NTAPI
924 MmGetPageProtect(PEPROCESS Process, PVOID Address)
925 {
926 ULONG Entry;
927 ULONG Protect;
928
929 Entry = MmGetPageEntryForProcess(Process, Address);
930
931
932 if (!(Entry & PA_PRESENT))
933 {
934 Protect = PAGE_NOACCESS;
935 }
936 else
937 {
938 if (Entry & PA_READWRITE)
939 {
940 Protect = PAGE_READWRITE;
941 }
942 else
943 {
944 Protect = PAGE_EXECUTE_READ;
945 }
946 if (Entry & PA_CD)
947 {
948 Protect |= PAGE_NOCACHE;
949 }
950 if (Entry & PA_WT)
951 {
952 Protect |= PAGE_WRITETHROUGH;
953 }
954 if (!(Entry & PA_USER))
955 {
956 Protect |= PAGE_SYSTEM;
957 }
958
959 }
960 return(Protect);
961 }
962
963 VOID
964 NTAPI
965 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
966 {
967 ULONG Attributes = 0;
968 PULONG Pt;
969 ULONG Pte;
970
971 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
972 Process, Address, flProtect);
973
974 Attributes = ProtectToPTE(flProtect);
975
976 Attributes &= 0xfff;
977 if (Address >= MmSystemRangeStart)
978 {
979 Attributes &= ~PA_USER;
980 }
981 else
982 {
983 Attributes |= PA_USER;
984 }
985
986 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
987 if (Pt == NULL)
988 {
989 KeBugCheck(MEMORY_MANAGEMENT);
990 }
991 Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
992
993 // We should be able to bring a page back from PAGE_NOACCESS
994 if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT))
995 {
996 DPRINT1("Invalid Pte %lx\n", Pte);
997 KeBugCheck(MEMORY_MANAGEMENT);
998 }
999
1000 if((Pte & Attributes) != Attributes)
1001 MiFlushTlb(Pt, Address);
1002 else
1003 MmUnmapPageTable(Pt);
1004 }
1005
1006 /*
1007 * @implemented
1008 */
1009 PHYSICAL_ADDRESS NTAPI
1010 MmGetPhysicalAddress(PVOID vaddr)
1011 /*
1012 * FUNCTION: Returns the physical address corresponding to a virtual address
1013 */
1014 {
1015 PHYSICAL_ADDRESS p;
1016 ULONG Pte;
1017
1018 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
1019 Pte = MmGetPageEntryForProcess(NULL, vaddr);
1020 if (Pte != 0 && (Pte & PA_PRESENT))
1021 {
1022 p.QuadPart = PAGE_MASK(Pte);
1023 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
1024 }
1025 else
1026 {
1027 p.QuadPart = 0;
1028 }
1029 return p;
1030 }
1031
1032 VOID
1033 INIT_FUNCTION
1034 NTAPI
1035 MmInitGlobalKernelPageDirectory(VOID)
1036 {
1037 /* Nothing to do here */
1038 }
1039
1040 /* EOF */