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