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