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