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