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