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