e66458918c1502ab6edc109c72902e5f09b250c0
[reactos.git] / reactos / ntoskrnl / mm / i386 / page.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: page.c,v 1.71 2004/08/19 21:47:51 hbirr Exp $
20 *
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/mm/i386/page.c
23 * PURPOSE: low level memory managment manipulation
24 * PROGRAMER: David Welch (welch@cwcom.net)
25 * UPDATE HISTORY:
26 * 9/3/98: Created
27 */
28
29 /* INCLUDES ***************************************************************/
30
31 #include <ntoskrnl.h>
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* GLOBALS *****************************************************************/
36
37 #define PA_BIT_PRESENT (0)
38 #define PA_BIT_READWRITE (1)
39 #define PA_BIT_USER (2)
40 #define PA_BIT_WT (3)
41 #define PA_BIT_CD (4)
42 #define PA_BIT_ACCESSED (5)
43 #define PA_BIT_DIRTY (6)
44 #define PA_BIT_GLOBAL (8)
45
46 #define PA_PRESENT (1 << PA_BIT_PRESENT)
47 #define PA_READWRITE (1 << PA_BIT_READWRITE)
48 #define PA_USER (1 << PA_BIT_USER)
49 #define PA_DIRTY (1 << PA_BIT_DIRTY)
50 #define PA_WT (1 << PA_BIT_WT)
51 #define PA_CD (1 << PA_BIT_CD)
52 #define PA_ACCESSED (1 << PA_BIT_ACCESSED)
53 #define PA_GLOBAL (1 << PA_BIT_GLOBAL)
54
55 #define PAGETABLE_MAP (0xf0000000)
56 #define PAGEDIRECTORY_MAP (0xf0000000 + (PAGETABLE_MAP / (1024)))
57
58 ULONG MmGlobalKernelPageDirectory[1024] = {0, };
59
60 #if defined(__GNUC__)
61 #define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT)
62 #define PFN_TO_PTE(X) ((X) << PAGE_SHIFT)
63 #define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X)))
64 #else
65 __inline LARGE_INTEGER PTE_TO_PAGE(ULONG npage)
66 {
67 LARGE_INTEGER dummy;
68 dummy.QuadPart = (LONGLONG)(PAGE_MASK(npage));
69 return dummy;
70 }
71 #endif
72
73 extern ULONG Ke386CpuidFlags;
74
75 /* FUNCTIONS ***************************************************************/
76
77 PULONG
78 MmGetPageDirectory(VOID)
79 {
80 unsigned int page_dir=0;
81 Ke386GetPageTableDirectory(page_dir);
82 return((PULONG)page_dir);
83 }
84
85 static ULONG
86 ProtectToPTE(ULONG flProtect)
87 {
88 ULONG Attributes = 0;
89
90 if (flProtect & (PAGE_NOACCESS|PAGE_GUARD))
91 {
92 Attributes = 0;
93 }
94 else if (flProtect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE))
95 {
96 Attributes = PA_PRESENT | PA_READWRITE;
97 }
98 else if (flProtect & (PAGE_READONLY|PAGE_EXECUTE|PAGE_EXECUTE_READ))
99 {
100 Attributes = PA_PRESENT;
101 }
102 else
103 {
104 DPRINT1("Unknown main protection type.\n");
105 KEBUGCHECK(0);
106 }
107 if (!(flProtect & PAGE_SYSTEM))
108 {
109 Attributes = Attributes | PA_USER;
110 }
111 if (flProtect & PAGE_NOCACHE)
112 {
113 Attributes = Attributes | PA_CD;
114 }
115 if (flProtect & PAGE_WRITETHROUGH)
116 {
117 Attributes = Attributes | PA_WT;
118 }
119 return(Attributes);
120 }
121
122 #define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (4 * 1024 * 1024))
123
124 #define ADDR_TO_PDE(v) (PULONG)(PAGEDIRECTORY_MAP + \
125 ((((ULONG)(v)) / (1024 * 1024))&(~0x3)))
126 #define ADDR_TO_PTE(v) (PULONG)(PAGETABLE_MAP + ((((ULONG)(v) / 1024))&(~0x3)))
127
128 #define ADDR_TO_PDE_OFFSET(v) ((((ULONG)(v)) / (4 * 1024 * 1024)))
129
130 NTSTATUS Mmi386ReleaseMmInfo(PEPROCESS Process)
131 {
132 PUSHORT LdtDescriptor;
133 ULONG LdtBase;
134
135 DPRINT("Mmi386ReleaseMmInfo(Process %x)\n",Process);
136
137 LdtDescriptor = (PUSHORT) &Process->Pcb.LdtDescriptor[0];
138 LdtBase = LdtDescriptor[1] |
139 ((LdtDescriptor[2] & 0xff) << 16) |
140 ((LdtDescriptor[3] & ~0xff) << 16);
141
142 DPRINT("LdtBase: %x\n", LdtBase);
143
144 if (LdtBase)
145 {
146 ExFreePool((PVOID) LdtBase);
147 }
148
149 MmReleasePageMemoryConsumer(MC_NPPOOL, Process->Pcb.DirectoryTableBase.QuadPart >> PAGE_SHIFT);
150 #if defined(__GNUC__)
151
152 Process->Pcb.DirectoryTableBase.QuadPart = 0LL;
153 #else
154
155 Process->Pcb.DirectoryTableBase.QuadPart = 0;
156 #endif
157
158 DPRINT("Finished Mmi386ReleaseMmInfo()\n");
159 return(STATUS_SUCCESS);
160 }
161
162 NTSTATUS MmCopyMmInfo(PEPROCESS Src, PEPROCESS Dest)
163 {
164 PHYSICAL_ADDRESS PhysPageDirectory;
165 PULONG PageDirectory;
166 PKPROCESS KProcess = &Dest->Pcb;
167
168 DPRINT("MmCopyMmInfo(Src %x, Dest %x)\n", Src, Dest);
169
170 PageDirectory = ExAllocatePage();
171 if (PageDirectory == NULL)
172 {
173 return(STATUS_UNSUCCESSFUL);
174 }
175 PhysPageDirectory = MmGetPhysicalAddress(PageDirectory);
176 KProcess->DirectoryTableBase = PhysPageDirectory;
177
178 memset(PageDirectory,0, ADDR_TO_PDE_OFFSET(KERNEL_BASE) * sizeof(ULONG));
179 memcpy(PageDirectory + ADDR_TO_PDE_OFFSET(KERNEL_BASE),
180 MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(KERNEL_BASE),
181 (1024 - ADDR_TO_PDE_OFFSET(KERNEL_BASE)) * sizeof(ULONG));
182
183 DPRINT("Addr %x\n",PAGETABLE_MAP / (4*1024*1024));
184 PageDirectory[PAGETABLE_MAP / (4*1024*1024)] =
185 PhysPageDirectory.u.LowPart | PA_PRESENT | PA_READWRITE;
186
187 ExUnmapPage(PageDirectory);
188
189 DPRINT("Finished MmCopyMmInfo()\n");
190 return(STATUS_SUCCESS);
191 }
192
193 VOID MmDeletePageTable(PEPROCESS Process, PVOID Address)
194 {
195 PEPROCESS CurrentProcess = PsGetCurrentProcess();
196
197 if (Process != NULL && Process != CurrentProcess)
198 {
199 KeAttachProcess(Process);
200 }
201 *(ADDR_TO_PDE(Address)) = 0;
202 if (Address >= (PVOID)KERNEL_BASE)
203 {
204 KEBUGCHECK(0);
205 // MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)] = 0;
206 }
207 FLUSH_TLB;
208 if (Process != NULL && Process != CurrentProcess)
209 {
210 KeDetachProcess();
211 }
212 }
213
214 VOID MmFreePageTable(PEPROCESS Process, PVOID Address)
215 {
216 PEPROCESS CurrentProcess = PsGetCurrentProcess();
217 PULONG PageTable;
218 ULONG i;
219 ULONG npage;
220
221 if (Process != NULL && Process != CurrentProcess)
222 {
223 KeAttachProcess(Process);
224 }
225
226 PageTable = (PULONG)PAGE_ROUND_DOWN((PVOID)ADDR_TO_PTE(Address));
227 for (i = 0; i < 1024; i++)
228 {
229 if (PageTable[i] != 0)
230 {
231 DbgPrint("Page table entry not clear at %x/%x (is %x)\n",
232 ((ULONG)Address / 4*1024*1024), i, PageTable[i]);
233 KEBUGCHECK(0);
234 }
235 }
236 npage = *(ADDR_TO_PDE(Address));
237 *(ADDR_TO_PDE(Address)) = 0;
238 FLUSH_TLB;
239
240 if (Address >= (PVOID)KERNEL_BASE)
241 {
242 // MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)] = 0;
243 KEBUGCHECK(0);
244 }
245 else
246 {
247 MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(npage));
248 }
249 if (Process != NULL && Process != CurrentProcess)
250 {
251 KeDetachProcess();
252 }
253 }
254
255 static PULONG MmGetPageEntry(PVOID PAddress, BOOL CreatePde)
256 /*
257 * FUNCTION: Get a pointer to the page table entry for a virtual address
258 */
259 {
260 PULONG Pde, kePde;
261 PFN_TYPE Pfn;
262 ULONG Attributes;
263 NTSTATUS Status;
264
265 DPRINT("MmGetPageEntry(Address %x)\n", PAddress);
266
267 Pde = ADDR_TO_PDE(PAGE_ROUND_DOWN(PAddress));
268 if (*Pde == 0)
269 {
270 if (PAddress >= (PVOID)KERNEL_BASE)
271 {
272 kePde = MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(PAddress);
273 if (*kePde == 0)
274 {
275 if (CreatePde == FALSE)
276 {
277 return NULL;
278 }
279 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
280 if (!NT_SUCCESS(Status) || Pfn == 0)
281 {
282 KEBUGCHECK(0);
283 }
284 Attributes = PA_PRESENT | PA_READWRITE;
285 if (Ke386CpuidFlags & X86_FEATURE_PGE)
286 {
287 Attributes |= PA_GLOBAL;
288 }
289 if (0 == InterlockedCompareExchange(kePde, PFN_TO_PTE(Pfn) | Attributes, 0))
290 {
291 Pfn = 0;
292 }
293 }
294 else
295 {
296 Pfn = 0;
297 }
298 *Pde = *kePde;
299 #if 0
300 /* Non existing mappings are not cached within the tlb. We must not invalidate this entry */
301 FLUSH_TLB_ONE(ADDR_TO_PTE(PAddress));
302 #endif
303 }
304 else
305 {
306 if (CreatePde == FALSE)
307 {
308 return NULL;
309 }
310 Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
311 if (!NT_SUCCESS(Status) || Pfn == 0)
312 {
313 KEBUGCHECK(0);
314 }
315 if (0 == InterlockedCompareExchange(Pde, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0))
316 {
317 Pfn = 0;
318 }
319 #if 0
320 /* Non existing mappings are not cached within the tlb. We must not invalidate this entry */
321 FLUSH_TLB_ONE(ADDR_TO_PTE(PAddress));
322 #endif
323 }
324 if (Pfn != 0)
325 {
326 MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
327 }
328 }
329 return (PULONG)ADDR_TO_PTE(PAddress);
330 }
331
332 static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address)
333 {
334 PULONG Pte;
335 ULONG oldPte;
336 PEPROCESS CurrentProcess = PsGetCurrentProcess();
337
338 if (Process != NULL && Process != CurrentProcess)
339 {
340 KeAttachProcess(Process);
341 }
342 Pte = MmGetPageEntry(Address, FALSE);
343 oldPte = Pte != NULL ? *Pte : 0;
344 if (Process != NULL && Process != CurrentProcess)
345 {
346 KeDetachProcess();
347 }
348 return oldPte;
349 }
350
351 PFN_TYPE
352 MmGetPfnForProcess(PEPROCESS Process,
353 PVOID Address)
354 {
355 ULONG Entry;
356
357 Entry = MmGetPageEntryForProcess(Process, Address);
358
359 if (!(Entry & PA_PRESENT))
360 {
361 return 0;
362 }
363 return(PTE_TO_PFN(Entry));
364 }
365
366 VOID
367 MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOL* WasDirty, PPFN_TYPE Page)
368 /*
369 * FUNCTION: Delete a virtual mapping
370 */
371 {
372 PULONG Pte;
373 ULONG oldPte;
374 PEPROCESS CurrentProcess = PsGetCurrentProcess();
375 BOOLEAN WasValid;
376
377 /*
378 * If we are setting a page in another process we need to be in its
379 * context.
380 */
381 if (Process != NULL && Process != CurrentProcess)
382 {
383 KeAttachProcess(Process);
384 }
385
386 Pte = MmGetPageEntry(Address, FALSE);
387 if (Pte == NULL)
388 {
389 KEBUGCHECK(0);
390 }
391
392 /*
393 * Atomically set the entry to zero and get the old value.
394 */
395 oldPte = *Pte;
396 *Pte = oldPte & (~PA_PRESENT);
397 FLUSH_TLB_ONE(Address);
398 WasValid = (PAGE_MASK(oldPte) != 0);
399 if (!WasValid)
400 {
401 KEBUGCHECK(0);
402 }
403
404 /*
405 * If necessary go back to the original context
406 */
407 if (Process != NULL && Process != CurrentProcess)
408 {
409 KeDetachProcess();
410 }
411
412 /*
413 * Return some information to the caller
414 */
415 if (WasDirty != NULL)
416 {
417 *WasDirty = oldPte & PA_DIRTY;
418 }
419 if (Page != NULL)
420 {
421 *Page = PTE_TO_PFN(oldPte);
422 }
423 }
424
425 VOID
426 MmRawDeleteVirtualMapping(PVOID Address)
427 {
428 PULONG Pte;
429
430 /*
431 * Set the page directory entry, we may have to copy the entry from
432 * the global page directory.
433 */
434
435 Pte = MmGetPageEntry(Address, FALSE);
436 if (Pte)
437 {
438 /*
439 * Set the entry to zero
440 */
441 *Pte = 0;
442 FLUSH_TLB_ONE(Address);
443 }
444 }
445
446 VOID
447 MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOL FreePage,
448 BOOL* WasDirty, PPFN_TYPE Page)
449 /*
450 * FUNCTION: Delete a virtual mapping
451 */
452 {
453 ULONG oldPte;
454 PULONG Pte;
455 PEPROCESS CurrentProcess = PsGetCurrentProcess();
456 BOOLEAN WasValid;
457
458 /*
459 * If we are setting a page in another process we need to be in its
460 * context.
461 */
462 if (Process != NULL && Process != CurrentProcess)
463 {
464 KeAttachProcess(Process);
465 }
466
467 Pte = MmGetPageEntry(Address, FALSE);
468 if (Pte == NULL)
469 {
470 if (Process != NULL && Process != CurrentProcess)
471 {
472 KeDetachProcess();
473 }
474 if (WasDirty != NULL)
475 {
476 *WasDirty = FALSE;
477 }
478 if (Page != NULL)
479 {
480 *Page = 0;
481 }
482 return;
483 }
484
485 /*
486 * Atomically set the entry to zero and get the old value.
487 */
488 oldPte = InterlockedExchange(Pte, 0);
489 if (oldPte)
490 {
491 FLUSH_TLB_ONE(Address);
492 }
493 WasValid = (PAGE_MASK(oldPte) != 0);
494 if (WasValid)
495 {
496 MmMarkPageUnmapped(PTE_TO_PFN(oldPte));
497 }
498 if (FreePage && WasValid)
499 {
500 MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(oldPte));
501 }
502
503 /*
504 * Decrement the reference count for this page table.
505 */
506 if (Process != NULL && WasValid &&
507 Process->AddressSpace.PageTableRefCountTable != NULL &&
508 Address < (PVOID)KERNEL_BASE)
509 {
510 PUSHORT Ptrc;
511
512 Ptrc = Process->AddressSpace.PageTableRefCountTable;
513
514 Ptrc[ADDR_TO_PAGE_TABLE(Address)]--;
515 if (Ptrc[ADDR_TO_PAGE_TABLE(Address)] == 0)
516 {
517 MmFreePageTable(Process, Address);
518 }
519 }
520
521 /*
522 * If necessary go back to the original context
523 */
524 if (Process != NULL && Process != CurrentProcess)
525 {
526 KeDetachProcess();
527 }
528
529 /*
530 * Return some information to the caller
531 */
532 if (WasDirty != NULL)
533 {
534 *WasDirty = oldPte & PA_DIRTY ? TRUE : FALSE;
535 }
536 if (Page != NULL)
537 {
538 *Page = PTE_TO_PFN(oldPte);
539 }
540 }
541
542 VOID
543 MmDeletePageFileMapping(PEPROCESS Process, PVOID Address,
544 SWAPENTRY* SwapEntry)
545 /*
546 * FUNCTION: Delete a virtual mapping
547 */
548 {
549 ULONG oldPte;
550 PULONG Pte;
551 PEPROCESS CurrentProcess = PsGetCurrentProcess();
552 BOOLEAN WasValid = FALSE;
553
554 /*
555 * If we are setting a page in another process we need to be in its
556 * context.
557 */
558 if (Process != NULL && Process != CurrentProcess)
559 {
560 KeAttachProcess(Process);
561 }
562
563 Pte = MmGetPageEntry(Address, FALSE);
564
565 if (Pte == NULL)
566 {
567 if (Process != NULL && Process != CurrentProcess)
568 {
569 KeDetachProcess();
570 }
571 *SwapEntry = 0;
572 return;
573 }
574
575 /*
576 * Atomically set the entry to zero and get the old value.
577 */
578 oldPte = InterlockedExchange(Pte, 0);
579 FLUSH_TLB_ONE(Address);
580
581 WasValid = PAGE_MASK(oldPte) == 0 ? FALSE : TRUE;
582
583 /*
584 * Decrement the reference count for this page table.
585 */
586 if (Process != NULL && WasValid &&
587 Process->AddressSpace.PageTableRefCountTable != NULL &&
588 Address < (PVOID)KERNEL_BASE)
589 {
590 PUSHORT Ptrc;
591
592 Ptrc = Process->AddressSpace.PageTableRefCountTable;
593
594 Ptrc[ADDR_TO_PAGE_TABLE(Address)]--;
595 if (Ptrc[ADDR_TO_PAGE_TABLE(Address)] == 0)
596 {
597 MmFreePageTable(Process, Address);
598 }
599 }
600
601 /*
602 * If necessary go back to the original context
603 */
604 if (Process != NULL && Process != CurrentProcess)
605 {
606 KeDetachProcess();
607 }
608
609 /*
610 * Return some information to the caller
611 */
612 *SwapEntry = oldPte >> 1;
613 }
614
615 BOOLEAN
616 Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
617 {
618 PULONG Pte, Pde;
619
620 Pde = ADDR_TO_PDE(PAddress);
621 if (*Pde == 0)
622 {
623 Pte = MmGetPageEntry(PAddress, FALSE);
624 #if 0
625 /* Non existing mappings are not cached within the tlb. We must not invalidate this entry */
626 FLASH_TLB_ONE(PAddress);
627 #endif
628 if (Pte != NULL)
629 {
630 return TRUE;
631 }
632 }
633 return(FALSE);
634 }
635
636 BOOLEAN MmIsDirtyPage(PEPROCESS Process, PVOID Address)
637 {
638 return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE;
639 }
640
641 BOOLEAN
642 MmIsAccessedAndResetAccessPage(PEPROCESS Process, PVOID Address)
643 {
644 ULONG oldPte;
645 PULONG Pte;
646 PEPROCESS CurrentProcess;
647
648 if (Process)
649 {
650 CurrentProcess = PsGetCurrentProcess();
651 if (Process != CurrentProcess)
652 {
653 KeAttachProcess(Process);
654 }
655 }
656 else
657 {
658 if (((ULONG)Address & ~0xFFF) < KERNEL_BASE)
659 {
660 DPRINT1("MmIsAccessedAndResetAccessPage is called for user space without a process.\n");
661 KEBUGCHECK(0);
662 }
663 CurrentProcess = NULL;
664 }
665
666 Pte = MmGetPageEntry(Address, FALSE);
667 if (Pte == NULL)
668 {
669 KEBUGCHECK(0);
670 }
671 oldPte = *Pte;
672 if (oldPte & PA_ACCESSED)
673 {
674 *Pte = *Pte & (~PA_ACCESSED);
675 FLUSH_TLB_ONE(Address);
676 }
677 if (Process != CurrentProcess)
678 {
679 KeDetachProcess();
680 }
681
682 return oldPte & PA_ACCESSED ? TRUE : FALSE;
683 }
684
685 VOID MmSetCleanPage(PEPROCESS Process, PVOID Address)
686 {
687 PULONG Pte;
688 PEPROCESS CurrentProcess;
689
690 if (Process)
691 {
692 CurrentProcess = PsGetCurrentProcess();
693 if (Process != CurrentProcess)
694 {
695 KeAttachProcess(Process);
696 }
697 }
698 else
699 {
700 if (Address < (PVOID)KERNEL_BASE)
701 {
702 DPRINT1("MmSetCleanPage is called for user space without a process.\n");
703 KEBUGCHECK(0);
704 }
705 CurrentProcess = NULL;
706 }
707 Pte = MmGetPageEntry(Address, FALSE);
708 if (Pte == NULL)
709 {
710 KEBUGCHECK(0);
711 }
712 if (*Pte & PA_DIRTY)
713 {
714 *Pte = *Pte & (~PA_DIRTY);
715 FLUSH_TLB_ONE(Address);
716 }
717 if (Process != CurrentProcess)
718 {
719 KeDetachProcess();
720 }
721 }
722
723 VOID MmSetDirtyPage(PEPROCESS Process, PVOID Address)
724 {
725 PULONG Pte;
726 PEPROCESS CurrentProcess = NULL;
727
728 if (Process)
729 {
730 CurrentProcess = PsGetCurrentProcess();
731 if (Process != CurrentProcess)
732 {
733 KeAttachProcess(Process);
734 }
735 }
736 else
737 {
738 if (Address < (PVOID)KERNEL_BASE)
739 {
740 DPRINT1("MmSetDirtyPage is called for user space without a process.\n");
741 KEBUGCHECK(0);
742 }
743 CurrentProcess = NULL;
744 }
745 Pte = MmGetPageEntry(Address, FALSE);
746 if (Pte == NULL)
747 {
748 KEBUGCHECK(0);
749 }
750 if (!(*Pte & PA_DIRTY))
751 {
752 *Pte = *Pte | PA_DIRTY;
753 FLUSH_TLB_ONE(Address);
754 }
755 if (Process != CurrentProcess)
756 {
757 KeDetachProcess();
758 }
759 }
760
761 VOID MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
762 {
763 PULONG Pte;
764 PEPROCESS CurrentProcess = PsGetCurrentProcess();
765
766 if (Process != CurrentProcess)
767 {
768 KeAttachProcess(Process);
769 }
770 Pte = MmGetPageEntry(Address, FALSE);
771 if (Pte == NULL)
772 {
773 KEBUGCHECK(0);
774 }
775 if (!(*Pte & PA_PRESENT))
776 {
777 *Pte = *Pte | PA_PRESENT;
778 FLUSH_TLB_ONE(Address);
779 }
780 if (Process != CurrentProcess)
781 {
782 KeDetachProcess();
783 }
784 }
785
786 BOOLEAN MmIsPagePresent(PEPROCESS Process, PVOID Address)
787 {
788 return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT ? TRUE : FALSE;
789 }
790
791 BOOLEAN MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
792 {
793 ULONG Entry;
794 Entry = MmGetPageEntryForProcess(Process, Address);
795 return !(Entry & PA_PRESENT) && Entry != 0 ? TRUE : FALSE;
796 }
797
798 NTSTATUS
799 MmCreateVirtualMappingForKernel(PVOID Address,
800 ULONG flProtect,
801 PPFN_TYPE Pages,
802 ULONG PageCount)
803 {
804 ULONG Attributes, oldPte;
805 PULONG Pte;
806 ULONG i;
807 PVOID Addr;
808
809 if (Address < (PVOID)KERNEL_BASE)
810 {
811 DPRINT1("MmCreateVirtualMappingForKernel is called for user space\n");
812 KEBUGCHECK(0);
813 }
814
815 Attributes = ProtectToPTE(flProtect);
816 if (Ke386CpuidFlags & X86_FEATURE_PGE)
817 {
818 Attributes |= PA_GLOBAL;
819 }
820 Addr = Address;
821 for (i = 0; i < PageCount; i++, Addr += PAGE_SIZE)
822 {
823 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
824 {
825 DPRINT1("Setting physical address but not allowing access at address "
826 "0x%.8X with attributes %x/%x.\n",
827 Addr, Attributes, flProtect);
828 KEBUGCHECK(0);
829 }
830
831 Pte = MmGetPageEntry(Addr, TRUE);
832 if (Pte == NULL)
833 {
834 KEBUGCHECK(0);
835 }
836 oldPte = *Pte;
837 if (PAGE_MASK((oldPte)) != 0)
838 {
839 KEBUGCHECK(0);
840 }
841 *Pte = PFN_TO_PTE(Pages[i]) | Attributes;
842 if (oldPte != 0)
843 {
844 FLUSH_TLB_ONE(Addr);
845 }
846 }
847
848 return(STATUS_SUCCESS);
849 }
850
851 NTSTATUS
852 MmCreatePageFileMapping(PEPROCESS Process,
853 PVOID Address,
854 SWAPENTRY SwapEntry)
855 {
856 PEPROCESS CurrentProcess;
857 PULONG Pte;
858 ULONG oldPte;
859
860 if (Process != NULL)
861 {
862 CurrentProcess = PsGetCurrentProcess();
863 }
864 else
865 {
866 CurrentProcess = NULL;
867 }
868
869 if (Process == NULL && Address < (PVOID)KERNEL_BASE)
870 {
871 DPRINT1("No process\n");
872 KEBUGCHECK(0);
873 }
874 if (Process != NULL && Address >= (PVOID)KERNEL_BASE)
875 {
876 DPRINT1("Setting kernel address with process context\n");
877 KEBUGCHECK(0);
878 }
879 if (SwapEntry & (1 << 31))
880 {
881 KEBUGCHECK(0);
882 }
883
884 if (Process != NULL && Process != CurrentProcess)
885 {
886 KeAttachProcess(Process);
887 }
888 Pte = MmGetPageEntry(Address, TRUE);
889 if (Pte == NULL)
890 {
891 KEBUGCHECK(0);
892 }
893 oldPte = *Pte;
894 if (PAGE_MASK((oldPte)) != 0)
895 {
896 MmMarkPageUnmapped(PTE_TO_PFN((oldPte)));
897 }
898 *Pte = SwapEntry << 1;
899 if (Process != NULL &&
900 Process->AddressSpace.PageTableRefCountTable != NULL &&
901 Address < (PVOID)KERNEL_BASE)
902 {
903 PUSHORT Ptrc;
904
905 Ptrc = Process->AddressSpace.PageTableRefCountTable;
906
907 Ptrc[ADDR_TO_PAGE_TABLE(Address)]++;
908 }
909 if (oldPte != 0)
910 {
911 FLUSH_TLB_ONE(Address);
912 }
913 if (Process != NULL && Process != CurrentProcess)
914 {
915 KeDetachProcess();
916 }
917 return(STATUS_SUCCESS);
918 }
919
920
921 NTSTATUS
922 MmCreateVirtualMappingUnsafe(PEPROCESS Process,
923 PVOID Address,
924 ULONG flProtect,
925 PPFN_TYPE Pages,
926 ULONG PageCount)
927 {
928 PEPROCESS CurrentProcess;
929 ULONG Attributes;
930 PVOID Addr = Address;
931 ULONG i;
932 PULONG Pte;
933 ULONG oldPte;
934
935 if (Process != NULL)
936 {
937 CurrentProcess = PsGetCurrentProcess();
938 }
939 else
940 {
941 CurrentProcess = NULL;
942 }
943
944 if (Process == NULL && Address < (PVOID)KERNEL_BASE)
945 {
946 DPRINT1("No process\n");
947 KEBUGCHECK(0);
948 }
949 if (Process != NULL && Address >= (PVOID)KERNEL_BASE)
950 {
951 DPRINT1("Setting kernel address with process context\n");
952 KEBUGCHECK(0);
953 }
954
955 Attributes = ProtectToPTE(flProtect);
956 if (Address >= (PVOID)KERNEL_BASE)
957 {
958 Attributes &= ~PA_USER;
959 if (Ke386CpuidFlags & X86_FEATURE_PGE)
960 {
961 Attributes |= PA_GLOBAL;
962 }
963 }
964 else
965 {
966 Attributes |= PA_USER;
967 }
968
969 if (Process != NULL && Process != CurrentProcess)
970 {
971 KeAttachProcess(Process);
972 }
973
974 for (i = 0; i < PageCount; i++, Addr += PAGE_SIZE)
975 {
976 Pte = MmGetPageEntry(Addr, TRUE);
977 if (Pte == NULL)
978 {
979 KEBUGCHECK(0);
980 }
981 if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
982 {
983 DPRINT1("Setting physical address but not allowing access at address "
984 "0x%.8X with attributes %x/%x.\n",
985 Addr, Attributes, flProtect);
986 KEBUGCHECK(0);
987 }
988 oldPte = *Pte;
989 MmMarkPageMapped(Pages[i]);
990 if (PAGE_MASK((oldPte)) != 0 && !((oldPte) & PA_PRESENT))
991 {
992 KEBUGCHECK(0);
993 }
994 if (PAGE_MASK((oldPte)) != 0)
995 {
996 MmMarkPageUnmapped(PTE_TO_PFN((oldPte)));
997 }
998 *Pte = PFN_TO_PTE(Pages[i]) | Attributes;
999 if (Address < (PVOID)KERNEL_BASE &&
1000 Process->AddressSpace.PageTableRefCountTable != NULL &&
1001 Attributes & PA_PRESENT)
1002 {
1003 PUSHORT Ptrc;
1004
1005 Ptrc = Process->AddressSpace.PageTableRefCountTable;
1006
1007 Ptrc[ADDR_TO_PAGE_TABLE(Addr)]++;
1008 }
1009 if (oldPte != 0)
1010 {
1011 FLUSH_TLB_ONE(Addr);
1012 }
1013 }
1014 if (Process != NULL && Process != CurrentProcess)
1015 {
1016 KeDetachProcess();
1017 }
1018 return(STATUS_SUCCESS);
1019 }
1020
1021 NTSTATUS
1022 MmCreateVirtualMapping(PEPROCESS Process,
1023 PVOID Address,
1024 ULONG flProtect,
1025 PPFN_TYPE Pages,
1026 ULONG PageCount)
1027 {
1028 ULONG i;
1029
1030 for (i = 0; i < PageCount; i++)
1031 {
1032 if (!MmIsUsablePage(Pages[i]))
1033 {
1034 DPRINT1("Page at address %x not usable\n", Pages[i] << PAGE_SHIFT);
1035 KEBUGCHECK(0);
1036 }
1037 }
1038
1039 return(MmCreateVirtualMappingUnsafe(Process,
1040 Address,
1041 flProtect,
1042 Pages,
1043 PageCount));
1044 }
1045
1046 ULONG
1047 MmGetPageProtect(PEPROCESS Process, PVOID Address)
1048 {
1049 ULONG Entry;
1050 ULONG Protect;
1051
1052 Entry = MmGetPageEntryForProcess(Process, Address);
1053
1054 if (!(Entry & PA_PRESENT))
1055 {
1056 Protect = PAGE_NOACCESS;
1057 }
1058 else
1059 {
1060 if (Entry & PA_READWRITE)
1061 {
1062 Protect = PAGE_READWRITE;
1063 }
1064 else
1065 {
1066 Protect = PAGE_EXECUTE_READ;
1067 }
1068 if (Entry & PA_CD)
1069 {
1070 Protect |= PAGE_NOCACHE;
1071 }
1072 if (Entry & PA_WT)
1073 {
1074 Protect |= PAGE_WRITETHROUGH;
1075 }
1076 if (!(Entry & PA_USER))
1077 {
1078 Protect |= PAGE_SYSTEM;
1079 }
1080
1081 }
1082 return(Protect);
1083 }
1084
1085 VOID
1086 MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
1087 {
1088 ULONG Attributes = 0;
1089 PULONG Pte;
1090 PEPROCESS CurrentProcess = PsGetCurrentProcess();
1091
1092 DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
1093 Process, Address, flProtect);
1094
1095 Attributes = ProtectToPTE(flProtect);
1096 if (Address >= (PVOID)KERNEL_BASE)
1097 {
1098 Attributes &= ~PA_USER;
1099 if (Ke386CpuidFlags & X86_FEATURE_PGE)
1100 {
1101 Attributes |= PA_GLOBAL;
1102 }
1103 }
1104 else
1105 {
1106 Attributes |= PA_USER;
1107 }
1108 if (Process != NULL && Process != CurrentProcess)
1109 {
1110 KeAttachProcess(Process);
1111 }
1112 Pte = MmGetPageEntry(Address, TRUE);
1113 if (Pte == NULL)
1114 {
1115 KEBUGCHECK(0);
1116 }
1117 *Pte = PAGE_MASK(*Pte) | Attributes | (*Pte & (PA_ACCESSED|PA_DIRTY));
1118 FLUSH_TLB_ONE(Address);
1119 if (Process != NULL && Process != CurrentProcess)
1120 {
1121 KeDetachProcess();
1122 }
1123 }
1124
1125 /*
1126 * @implemented
1127 */
1128 PHYSICAL_ADDRESS STDCALL
1129 MmGetPhysicalAddress(PVOID vaddr)
1130 /*
1131 * FUNCTION: Returns the physical address corresponding to a virtual address
1132 */
1133 {
1134 PHYSICAL_ADDRESS p;
1135 PULONG Pte;
1136
1137 DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr);
1138
1139 Pte = MmGetPageEntry(vaddr, FALSE);
1140 if (Pte != NULL && *Pte & PA_PRESENT)
1141 {
1142 p.QuadPart = PAGE_MASK(*Pte);
1143 p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1);
1144 }
1145 else
1146 {
1147 p.QuadPart = 0;
1148 }
1149
1150 return p;
1151 }
1152
1153 VOID MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
1154 {
1155 PULONG Pde;
1156 ULONG StartOffset, EndOffset, Offset;
1157
1158 if (Address < (PVOID)KERNEL_BASE)
1159 {
1160 KEBUGCHECK(0);
1161 }
1162
1163 StartOffset = ADDR_TO_PDE_OFFSET(Address);
1164 EndOffset = ADDR_TO_PDE_OFFSET(Address + Size);
1165
1166 if (Process != NULL && Process != PsGetCurrentProcess())
1167 {
1168 Pde = ExAllocatePageWithPhysPage(Process->Pcb.DirectoryTableBase.u.LowPart >> PAGE_SHIFT);
1169 }
1170 else
1171 {
1172 Pde = (PULONG)PAGEDIRECTORY_MAP;
1173 }
1174 for (Offset = StartOffset; Offset <= EndOffset; Offset++)
1175 {
1176 if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP))
1177 {
1178 InterlockedCompareExchange(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0);
1179 }
1180 }
1181 if (Pde != (PULONG)PAGEDIRECTORY_MAP)
1182 {
1183 ExUnmapPage(Pde);
1184 }
1185 }
1186
1187 VOID INIT_FUNCTION
1188 MmInitGlobalKernelPageDirectory(VOID)
1189 {
1190 ULONG i;
1191 PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP;
1192
1193 for (i = ADDR_TO_PDE_OFFSET(KERNEL_BASE); i < 1024; i++)
1194 {
1195 if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&
1196 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i])
1197 {
1198 MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i];
1199 if (Ke386CpuidFlags & X86_FEATURE_PGE)
1200 {
1201 MmGlobalKernelPageDirectory[i] |= PA_GLOBAL;
1202 }
1203 }
1204 }
1205 }
1206
1207 /* EOF */