[PSDK]
[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 ULONG MmGlobalKernelPageDirectory[1024];
47
48 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
49 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
50
51 #if defined(__GNUC__)
52 #define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X)))
53 #else
54 __inline LARGE_INTEGER PTE_TO_PAGE(ULONG npage)
55 {
56 LARGE_INTEGER dummy;
57 dummy.QuadPart = (LONGLONG)(PAGE_MASK(npage));
58 return dummy;
59 }
60 #endif
61
62 const
63 ULONG
64 MmProtectToPteMask[32] =
65 {
66 //
67 // These are the base MM_ protection flags
68 //
69 0,
70 PTE_READONLY | PTE_ENABLE_CACHE,
71 PTE_EXECUTE | PTE_ENABLE_CACHE,
72 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
73 PTE_READWRITE | PTE_ENABLE_CACHE,
74 PTE_WRITECOPY | PTE_ENABLE_CACHE,
75 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
76 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
77 //
78 // These OR in the MM_NOCACHE flag
79 //
80 0,
81 PTE_READONLY | PTE_DISABLE_CACHE,
82 PTE_EXECUTE | PTE_DISABLE_CACHE,
83 PTE_EXECUTE_READ | PTE_DISABLE_CACHE,
84 PTE_READWRITE | PTE_DISABLE_CACHE,
85 PTE_WRITECOPY | PTE_DISABLE_CACHE,
86 PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE,
87 PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE,
88 //
89 // These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
90 //
91 0,
92 PTE_READONLY | PTE_ENABLE_CACHE,
93 PTE_EXECUTE | PTE_ENABLE_CACHE,
94 PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
95 PTE_READWRITE | PTE_ENABLE_CACHE,
96 PTE_WRITECOPY | PTE_ENABLE_CACHE,
97 PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
98 PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
99 //
100 // These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
101 //
102 0,
103 PTE_READONLY | PTE_WRITECOMBINED_CACHE,
104 PTE_EXECUTE | PTE_WRITECOMBINED_CACHE,
105 PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE,
106 PTE_READWRITE | PTE_WRITECOMBINED_CACHE,
107 PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
108 PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE,
109 PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
110 };
111
112 /* FUNCTIONS ***************************************************************/
113
114 BOOLEAN MmUnmapPageTable(PULONG Pt);
115
116 VOID
117 MiFlushTlb(PULONG Pt, PVOID Address)
118 {
119 if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart)
120 {
121 KeInvalidateTlbEntry(Address);
122 }
123 }
124
125 static ULONG
126 ProtectToPTE(ULONG flProtect)
127 {
128 ULONG Attributes = 0;
129
130 if (flProtect & (PAGE_NOACCESS|PAGE_GUARD))
131 {
132 Attributes = 0;
133 }
134 else if (flProtect & PAGE_IS_WRITABLE)
135 {
136 Attributes = PA_PRESENT | PA_READWRITE;
137 }
138 else if (flProtect & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE))
139 {
140 Attributes = PA_PRESENT;
141 }
142 else
143 {
144 DPRINT1("Unknown main protection type.\n");
145 KeBugCheck(MEMORY_MANAGEMENT);
146 }
147
148 if (flProtect & PAGE_SYSTEM)
149 {
150 }
151 else
152 {
153 Attributes = Attributes | PA_USER;
154 }
155 if (flProtect & PAGE_NOCACHE)
156 {
157 Attributes = Attributes | PA_CD;
158 }
159 if (flProtect & PAGE_WRITETHROUGH)
160 {
161 Attributes = Attributes | PA_WT;
162 }
163 return(Attributes);
164 }
165
166 static PULONG
167 MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create)
168 {
169 ULONG PdeOffset = ADDR_TO_PDE_OFFSET(Address);
170 NTSTATUS Status;
171 PFN_NUMBER Pfn;
172 ULONG Entry;
173 PULONG Pt, PageDir;
174
175 if (Address < MmSystemRangeStart && Process && Process != PsGetCurrentProcess())
176 {
177 PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
178 if (PageDir == NULL)
179 {
180 KeBugCheck(MEMORY_MANAGEMENT);
181 }
182 if (0 == InterlockedCompareExchangePte(&PageDir[PdeOffset], 0, 0))
183 {
184 if (Create == FALSE)
185 {
186 MmDeleteHyperspaceMapping(PageDir);
187 return NULL;
188 }
189 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
190 if (!NT_SUCCESS(Status) || Pfn == 0)
191 {
192 KeBugCheck(MEMORY_MANAGEMENT);
193 }
194 Entry = InterlockedCompareExchangePte(&PageDir[PdeOffset], PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
195 if (Entry != 0)
196 {
197 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
198 Pfn = PTE_TO_PFN(Entry);
199 }
200 }
201 else
202 {
203 Pfn = PTE_TO_PFN(PageDir[PdeOffset]);
204 }
205 MmDeleteHyperspaceMapping(PageDir);
206 Pt = MmCreateHyperspaceMapping(Pfn);
207 if (Pt == NULL)
208 {
209 KeBugCheck(MEMORY_MANAGEMENT);
210 }
211 return Pt + ADDR_TO_PTE_OFFSET(Address);
212 }
213 PageDir = (PULONG)MiAddressToPde(Address);
214 if (0 == InterlockedCompareExchangePte(PageDir, 0, 0))
215 {
216 if (Address >= MmSystemRangeStart)
217 {
218 if (0 == InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], 0, 0))
219 {
220 if (Create == FALSE)
221 {
222 return NULL;
223 }
224 Status = MmRequestPageMemoryConsumer(MC_SYSTEM, FALSE, &Pfn);
225 if (!NT_SUCCESS(Status) || Pfn == 0)
226 {
227 KeBugCheck(MEMORY_MANAGEMENT);
228 }
229 Entry = PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE;
230 if(0 != InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], Entry, 0))
231 {
232 MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
233 }
234 InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
235 RtlZeroMemory(MiPteToAddress(PageDir), PAGE_SIZE);
236 return (PULONG)MiAddressToPte(Address);
237 }
238 InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
239 }
240 else
241 {
242 if (Create == FALSE)
243 {
244 return NULL;
245 }
246 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
247 if (!NT_SUCCESS(Status) || Pfn == 0)
248 {
249 KeBugCheck(MEMORY_MANAGEMENT);
250 }
251 Entry = InterlockedCompareExchangePte(PageDir, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
252 if (Entry != 0)
253 {
254 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
255 }
256 }
257 }
258 return (PULONG)MiAddressToPte(Address);
259 }
260
261 BOOLEAN MmUnmapPageTable(PULONG Pt)
262 {
263 if (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024)
264 {
265 return TRUE;
266 }
267
268 if (Pt)
269 {
270 MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt));
271 }
272 return FALSE;
273 }
274
275 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
276 {
277 ULONG Pte;
278 PULONG Pt;
279
280 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
281 if (Pt)
282 {
283 Pte = *Pt;
284 MmUnmapPageTable(Pt);
285 return Pte;
286 }
287 return 0;
288 }
289
290 PFN_NUMBER
291 NTAPI
292 MmGetPfnForProcess(PEPROCESS Process,
293 PVOID Address)
294 {
295 ULONG Entry;
296 Entry = MmGetPageEntryForProcess(Process, Address);
297 if (!(Entry & PA_PRESENT))
298 {
299 return 0;
300 }
301 return(PTE_TO_PFN(Entry));
302 }
303
304 VOID
305 NTAPI
306 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_NUMBER Page)
307 /*
308 * FUNCTION: Delete a virtual mapping
309 */
310 {
311 BOOLEAN WasValid;
312 ULONG Pte;
313 PULONG Pt;
314
315 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
316 if (Pt == NULL)
317 {
318 KeBugCheck(MEMORY_MANAGEMENT);
319 }
320 /*
321 * Atomically disable the present bit and get the old value.
322 */
323 do
324 {
325 Pte = *Pt;
326 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_PRESENT, Pte));
327
328 MiFlushTlb(Pt, Address);
329 WasValid = (PAGE_MASK(Pte) != 0);
330 if (!WasValid)
331 {
332 KeBugCheck(MEMORY_MANAGEMENT);
333 }
334
335 /*
336 * Return some information to the caller
337 */
338 if (WasDirty != NULL)
339 {
340 *WasDirty = Pte & PA_DIRTY;
341 }
342 if (Page != NULL)
343 {
344 *Page = PTE_TO_PFN(Pte);
345 }
346 }
347
348 VOID
349 NTAPI
350 MmRawDeleteVirtualMapping(PVOID Address)
351 {
352 PULONG Pt;
353
354 Pt = MmGetPageTableForProcess(NULL, Address, FALSE);
355 if (Pt && *Pt)
356 {
357 /*
358 * Set the entry to zero
359 */
360 InterlockedExchangePte(Pt, 0);
361 MiFlushTlb(Pt, Address);
362 }
363 }
364
365 VOID
366 NTAPI
367 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage,
368 BOOLEAN* WasDirty, PPFN_NUMBER Page)
369 /*
370 * FUNCTION: Delete a virtual mapping
371 */
372 {
373 BOOLEAN WasValid = FALSE;
374 PFN_NUMBER Pfn;
375 ULONG Pte;
376 PULONG Pt;
377
378 DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
379 Process, Address, FreePage, WasDirty, Page);
380
381 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
382
383 if (Pt == NULL)
384 {
385 if (WasDirty != NULL)
386 {
387 *WasDirty = FALSE;
388 }
389 if (Page != NULL)
390 {
391 *Page = 0;
392 }
393 return;
394 }
395
396 /*
397 * Atomically set the entry to zero and get the old value.
398 */
399 Pte = InterlockedExchangePte(Pt, 0);
400
401 MiFlushTlb(Pt, Address);
402
403 WasValid = (PAGE_MASK(Pte) != 0);
404 if (WasValid)
405 {
406 Pfn = PTE_TO_PFN(Pte);
407 }
408 else
409 {
410 Pfn = 0;
411 }
412
413 if (FreePage && WasValid)
414 {
415 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
416 }
417
418 /*
419 * Return some information to the caller
420 */
421 if (WasDirty != NULL)
422 {
423 *WasDirty = Pte & PA_DIRTY ? TRUE : FALSE;
424 }
425 if (Page != NULL)
426 {
427 *Page = Pfn;
428 }
429 }
430
431 VOID
432 NTAPI
433 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
434 SWAPENTRY* SwapEntry)
435 /*
436 * FUNCTION: Delete a virtual mapping
437 */
438 {
439 ULONG Pte;
440 PULONG Pt;
441
442 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
443
444 if (Pt == NULL)
445 {
446 *SwapEntry = 0;
447 return;
448 }
449
450 /*
451 * Atomically set the entry to zero and get the old value.
452 */
453 Pte = InterlockedExchangePte(Pt, 0);
454
455 MiFlushTlb(Pt, Address);
456
457 /*
458 * Return some information to the caller
459 */
460 *SwapEntry = Pte >> 1;
461 }
462
463 BOOLEAN
464 Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
465 {
466 PULONG Pt, Pde;
467 Pde = (PULONG)MiAddressToPde(PAddress);
468 if (*Pde == 0)
469 {
470 Pt = MmGetPageTableForProcess(NULL, PAddress, FALSE);
471 if (Pt != NULL)
472 {
473 return TRUE;
474 }
475 }
476 return(FALSE);
477 }
478
479 BOOLEAN
480 NTAPI
481 MmIsDirtyPage(PEPROCESS Process, PVOID Address)
482 {
483 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
484 }
485
486 VOID
487 NTAPI
488 MmSetCleanPage(PEPROCESS Process, PVOID Address)
489 {
490 PULONG Pt;
491 ULONG Pte;
492
493 if (Address < MmSystemRangeStart && Process == NULL)
494 {
495 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
496 KeBugCheck(MEMORY_MANAGEMENT);
497 }
498
499 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
500
501 if (Pt == NULL)
502 {
503 KeBugCheck(MEMORY_MANAGEMENT);
504 }
505
506 do
507 {
508 Pte = *Pt;
509 } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_DIRTY, Pte));
510
511 if (Pte & PA_DIRTY)
512 {
513 MiFlushTlb(Pt, Address);
514 }
515 else
516 {
517 MmUnmapPageTable(Pt);
518 }
519 }
520
521 VOID
522 NTAPI
523 MmSetDirtyPage(PEPROCESS Process, PVOID Address)
524 {
525 PULONG Pt;
526 ULONG Pte;
527
528 if (Address < MmSystemRangeStart && Process == NULL)
529 {
530 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
531 KeBugCheck(MEMORY_MANAGEMENT);
532 }
533
534 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
535 if (Pt == NULL)
536 {
537 KeBugCheck(MEMORY_MANAGEMENT);
538 }
539
540 do
541 {
542 Pte = *Pt;
543 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_DIRTY, Pte));
544 if (!(Pte & PA_DIRTY))
545 {
546 MiFlushTlb(Pt, Address);
547 }
548 else
549 {
550 MmUnmapPageTable(Pt);
551 }
552 }
553
554 VOID
555 NTAPI
556 MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
557 {
558 PULONG Pt;
559 ULONG Pte;
560
561 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
562 if (Pt == NULL)
563 {
564 KeBugCheck(MEMORY_MANAGEMENT);
565 }
566
567 do
568 {
569 Pte = *Pt;
570 } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
571 if (!(Pte & PA_PRESENT))
572 {
573 MiFlushTlb(Pt, Address);
574 }
575 else
576 {
577 MmUnmapPageTable(Pt);
578 }
579 }
580
581 BOOLEAN
582 NTAPI
583 MmIsPagePresent(PEPROCESS Process, PVOID Address)
584 {
585 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
586 }
587
588 BOOLEAN
589 NTAPI
590 MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
591 {
592 ULONG Entry;
593 Entry = MmGetPageEntryForProcess(Process, Address);
594 return !(Entry & PA_PRESENT) && (Entry & 0x800) && Entry != 0;
595 }
596
597 NTSTATUS
598 NTAPI
599 MmCreatePageFileMapping(PEPROCESS Process,
600 PVOID Address,
601 SWAPENTRY SwapEntry)
602 {
603 PULONG Pt;
604 ULONG Pte;
605
606 if (Process == NULL && Address < MmSystemRangeStart)
607 {
608 DPRINT1("No process\n");
609 KeBugCheck(MEMORY_MANAGEMENT);
610 }
611 if (Process != NULL && Address >= MmSystemRangeStart)
612 {
613 DPRINT1("Setting kernel address with process context\n");
614 KeBugCheck(MEMORY_MANAGEMENT);
615 }
616
617 if (SwapEntry & (1 << 31))
618 {
619 KeBugCheck(MEMORY_MANAGEMENT);
620 }
621
622 Pt = MmGetPageTableForProcess(Process, Address, TRUE);
623 if (Pt == NULL)
624 {
625 KeBugCheck(MEMORY_MANAGEMENT);
626 }
627 Pte = *Pt;
628 InterlockedExchangePte(Pt, SwapEntry << 1);
629 if (Pte != 0)
630 {
631 MiFlushTlb(Pt, Address);
632 }
633 else
634 {
635 MmUnmapPageTable(Pt);
636 }
637
638 return(STATUS_SUCCESS);
639 }
640
641
642 NTSTATUS
643 NTAPI
644 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
645 PVOID Address,
646 ULONG flProtect,
647 PPFN_NUMBER Pages,
648 ULONG PageCount)
649 {
650 ULONG Attributes;
651 PVOID Addr;
652 ULONG i;
653 ULONG oldPdeOffset, PdeOffset;
654 PULONG Pt = NULL;
655 ULONG Pte;
656 BOOLEAN NoExecute = FALSE;
657
658 DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
659 Process, Address, flProtect, Pages, *Pages, PageCount);
660
661 if (Process == NULL)
662 {
663 if (Address < MmSystemRangeStart)
664 {
665 DPRINT1("No process\n");
666 KeBugCheck(MEMORY_MANAGEMENT);
667 }
668 if (PageCount > 0x10000 ||
669 (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
670 {
671 DPRINT1("Page count too large\n");
672 KeBugCheck(MEMORY_MANAGEMENT);
673 }
674 }
675 else
676 {
677 if (Address >= MmSystemRangeStart)
678 {
679 DPRINT1("Setting kernel address with process context\n");
680 KeBugCheck(MEMORY_MANAGEMENT);
681 }
682 if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
683 (ULONG_PTR) Address / PAGE_SIZE + PageCount >
684 (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
685 {
686 DPRINT1("Page Count too large\n");
687 KeBugCheck(MEMORY_MANAGEMENT);
688 }
689 }
690
691 Attributes = ProtectToPTE(flProtect);
692 if (Attributes & 0x80000000)
693 {
694 NoExecute = TRUE;
695 }
696 Attributes &= 0xfff;
697 if (Address >= MmSystemRangeStart)
698 {
699 Attributes &= ~PA_USER;
700 }
701 else
702 {
703 Attributes |= PA_USER;
704 }
705
706 Addr = Address;
707 oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1;
708 for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE))
709 {
710 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
711 {
712 DPRINT1("Setting physical address but not allowing access at address "
713 "0x%.8X with attributes %x/%x.\n",
714 Addr, Attributes, flProtect);
715 KeBugCheck(MEMORY_MANAGEMENT);
716 }
717 PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
718 if (oldPdeOffset != PdeOffset)
719 {
720 MmUnmapPageTable(Pt);
721 Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
722 if (Pt == NULL)
723 {
724 KeBugCheck(MEMORY_MANAGEMENT);
725 }
726 }
727 else
728 {
729 Pt++;
730 }
731 oldPdeOffset = PdeOffset;
732
733 Pte = *Pt;
734 if (PAGE_MASK(Pte) != 0 && !(Pte & PA_PRESENT) && (Pte & 0x800))
735 {
736 DPRINT1("Bad PTE %lx\n", Pte);
737 KeBugCheck(MEMORY_MANAGEMENT);
738 }
739 InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
740 if (Pte != 0)
741 {
742 if (Address > MmSystemRangeStart ||
743 (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024))
744 {
745 MiFlushTlb(Pt, Address);
746 }
747 }
748 }
749 if (Addr > Address)
750 {
751 MmUnmapPageTable(Pt);
752 }
753
754 return(STATUS_SUCCESS);
755 }
756
757 NTSTATUS
758 NTAPI
759 MmCreateVirtualMapping(PEPROCESS Process,
760 PVOID Address,
761 ULONG flProtect,
762 PPFN_NUMBER Pages,
763 ULONG PageCount)
764 {
765 ULONG i;
766
767 for (i = 0; i < PageCount; i++)
768 {
769 if (!MmIsPageInUse(Pages[i]))
770 {
771 DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
772 KeBugCheck(MEMORY_MANAGEMENT);
773 }
774 }
775
776 return(MmCreateVirtualMappingUnsafe(Process,
777 Address,
778 flProtect,
779 Pages,
780 PageCount));
781 }
782
783 ULONG
784 NTAPI
785 MmGetPageProtect(PEPROCESS Process, PVOID Address)
786 {
787 ULONG Entry;
788 ULONG Protect;
789
790 Entry = MmGetPageEntryForProcess(Process, Address);
791
792
793 if (!(Entry & PA_PRESENT))
794 {
795 Protect = PAGE_NOACCESS;
796 }
797 else
798 {
799 if (Entry & PA_READWRITE)
800 {
801 Protect = PAGE_READWRITE;
802 }
803 else
804 {
805 Protect = PAGE_EXECUTE_READ;
806 }
807 if (Entry & PA_CD)
808 {
809 Protect |= PAGE_NOCACHE;
810 }
811 if (Entry & PA_WT)
812 {
813 Protect |= PAGE_WRITETHROUGH;
814 }
815 if (!(Entry & PA_USER))
816 {
817 Protect |= PAGE_SYSTEM;
818 }
819
820 }
821 return(Protect);
822 }
823
824 VOID
825 NTAPI
826 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
827 {
828 ULONG Attributes = 0;
829 BOOLEAN NoExecute = FALSE;
830 PULONG Pt;
831
832 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
833 Process, Address, flProtect);
834
835 Attributes = ProtectToPTE(flProtect);
836
837 if (Attributes & 0x80000000)
838 {
839 NoExecute = TRUE;
840 }
841 Attributes &= 0xfff;
842 if (Address >= MmSystemRangeStart)
843 {
844 Attributes &= ~PA_USER;
845 }
846 else
847 {
848 Attributes |= PA_USER;
849 }
850
851 Pt = MmGetPageTableForProcess(Process, Address, FALSE);
852 if (Pt == NULL)
853 {
854 KeBugCheck(MEMORY_MANAGEMENT);
855 }
856 InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
857 MiFlushTlb(Pt, Address);
858 }
859
860 /*
861 * @implemented
862 */
863 PHYSICAL_ADDRESS NTAPI
864 MmGetPhysicalAddress(PVOID vaddr)
865 /*
866 * FUNCTION: Returns the physical address corresponding to a virtual address
867 */
868 {
869 PHYSICAL_ADDRESS p;
870 ULONG Pte;
871
872 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
873 Pte = MmGetPageEntryForProcess(NULL, vaddr);
874 if (Pte != 0 && Pte & PA_PRESENT)
875 {
876 p.QuadPart = PAGE_MASK(Pte);
877 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
878 }
879 else
880 {
881 p.QuadPart = 0;
882 }
883 return p;
884 }
885
886 VOID
887 NTAPI
888 MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
889 {
890 ULONG StartOffset, EndOffset, Offset;
891 PULONG Pde;
892
893 //
894 // Check if the process isn't there anymore
895 // This is probably a bad sign, since it means the caller is setting cr3 to
896 // 0 or something...
897 //
898 if ((PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]) == 0) && (Process != PsGetCurrentProcess()))
899 {
900 DPRINT1("Process: %16s is dead: %p\n", Process->ImageFileName, Process->Pcb.DirectoryTableBase[0]);
901 ASSERT(FALSE);
902 return;
903 }
904
905 if (Address < MmSystemRangeStart)
906 {
907 KeBugCheck(MEMORY_MANAGEMENT);
908 }
909
910 StartOffset = ADDR_TO_PDE_OFFSET(Address);
911 EndOffset = ADDR_TO_PDE_OFFSET((PVOID)((ULONG_PTR)Address + Size));
912
913 if (Process != NULL && Process != PsGetCurrentProcess())
914 {
915 Pde = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
916 }
917 else
918 {
919 Pde = (PULONG)PAGEDIRECTORY_MAP;
920 }
921 for (Offset = StartOffset; Offset <= EndOffset; Offset++)
922 {
923 if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP))
924 {
925 InterlockedCompareExchangePte(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0);
926 }
927 }
928 if (Pde != (PULONG)PAGEDIRECTORY_MAP)
929 {
930 MmDeleteHyperspaceMapping(Pde);
931 }
932 }
933
934 VOID
935 INIT_FUNCTION
936 NTAPI
937 MmInitGlobalKernelPageDirectory(VOID)
938 {
939 ULONG i;
940 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
941
942 DPRINT("MmInitGlobalKernelPageDirectory()\n");
943
944 for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++)
945 {
946 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
947 i != ADDR_TO_PDE_OFFSET(HYPERSPACE) &&
948 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
949 {
950 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
951 }
952 }
953 }
954
955 /* EOF */