Author: arty Date: Mon Jan 8 12:28:39 2007 New Revision: 25375
URL: http://svn.reactos.org/svn/reactos?rev=25375&view=rev Log: Start of an implementation of page.c
Added: branches/powerpc/reactos/ntoskrnl/mm/powerpc/ branches/powerpc/reactos/ntoskrnl/mm/powerpc/page.c
Added: branches/powerpc/reactos/ntoskrnl/mm/powerpc/page.c URL: http://svn.reactos.org/svn/reactos/branches/powerpc/reactos/ntoskrnl/mm/powe... ============================================================================== --- branches/powerpc/reactos/ntoskrnl/mm/powerpc/page.c (added) +++ branches/powerpc/reactos/ntoskrnl/mm/powerpc/page.c Mon Jan 8 12:28:39 2007 @@ -1,0 +1,1325 @@ +/* $Id: page.c 23907 2006-09-04 05:52:23Z arty $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: ntoskrnl/mm/i386/page.c + * PURPOSE: Low level memory managment manipulation + * + * PROGRAMMERS: David Welch (welch@cwcom.net) + * Revised for PowerPC by arty + */ + +/* INCLUDES ***************************************************************/ + +#include <ntoskrnl.h> +#define NDEBUG +#include <internal/debug.h> + +#if defined (ALLOC_PRAGMA) +#pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory) +#pragma alloc_text(INIT, MiInitPageDirectoryMap) +#endif + +/* GLOBALS *****************************************************************/ + +#define PAGETABLE_MAP (0xc0000000) +#define PAGEDIRECTORY_MAP (0xc0000000 + (PAGETABLE_MAP / (1024))) + +#define PA_PRESENT (1ll<<63) +#define PA_USER (1ll<<62) +#define PA_ACCESSED 0x200 +#define PA_DIRTY 0x100 +#define PA_WT 0x20 +#define PA_CD 0x10 +#define PA_READWRITE 3 + +#define HYPERSPACE (0xc0400000) +#define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPERSPACE && (ULONG)(v) < HYPERSPACE + 0x400000)) + +#define PP_FROM_PROTECT(X) \ + ((X & (PAGE_NOACCESS | PAGE_GUARD)) ? 0 : \ + (X & PAGE_IS_WRITABLE) ? 2 : \ + (X & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE)) ? 3 : 0) + +ULONG MmGlobalKernelPageDirectory[1024]; + +#define PTE_TO_PFN(X) ((X) >> PAGE_SHIFT) +#define PFN_TO_PTE(X) ((X) << PAGE_SHIFT) + +#if defined(__GNUC__) +#define PTE_TO_PAGE(X) ((LARGE_INTEGER)(LONGLONG)(PAGE_MASK(X))) +#else +__inline LARGE_INTEGER PTE_TO_PAGE(ULONG npage) +{ + LARGE_INTEGER dummy; + dummy.QuadPart = (LONGLONG)(PAGE_MASK(npage)); + return dummy; +} +#endif + +/* FUNCTIONS ***************************************************************/ + +BOOLEAN MmUnmapPageTable(PULONG Pt); + +VOID +STDCALL +MiFlushTlbIpiRoutine(PVOID Address) +{ + if (Address == (PVOID)0xffffffff) + { + KeFlushCurrentTb(); + } + else if (Address == (PVOID)0xfffffffe) + { + FLUSH_TLB; + } + else + { + FLUSH_TLB_ONE(Address); + } +} + +VOID +MiFlushTlb(PULONG Pt, PVOID Address) +{ +#ifdef CONFIG_SMP + if (Pt) + { + MmUnmapPageTable(Pt); + } + if (KeNumberProcessors>1) + { + KeIpiGenericCall(MiFlushTlbIpiRoutine, Address); + } + else + { + MiFlushTlbIpiRoutine(Address); + } +#else + if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart) + { + FLUSH_TLB_ONE(Address); + } +#endif +} + + + +PULONG +MmGetPageDirectory(VOID) +{ + unsigned int page_dir=0; + return((PULONG)page_dir); +} + +static ULONGLONG +ProtectToPTE(ULONG flProtect) +{ + return PP_FROM_PROTECT(flProtect); +} + +#define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (1024 * PAGE_SIZE)) + +#define ADDR_TO_PDE(v) (PULONG)(PAGEDIRECTORY_MAP + \ + ((((ULONG)(v)) / (1024 * 1024))&(~0x3))) +#define ADDR_TO_PTE(v) (PULONG)(PAGETABLE_MAP + ((((ULONG)(v) / 1024))&(~0x3))) + +#define ADDR_TO_PDE_OFFSET(v) ((((ULONG)(v)) / (1024 * PAGE_SIZE))) + +#define ADDR_TO_PTE_OFFSET(v) ((((ULONG)(v)) % (1024 * PAGE_SIZE)) / PAGE_SIZE) + +NTSTATUS +STDCALL +MmCopyMmInfo(PEPROCESS Src, + PEPROCESS Dest, + PPHYSICAL_ADDRESS DirectoryTableBase) +{ + NTSTATUS Status; + ULONG i, j; + PFN_TYPE Pfn[7]; + ULONG Count; + PULONG PageDirectory; + + DPRINT("MmCopyMmInfo(Src %x, Dest %x)\n", Src, Dest); + + Count = 2; + + for (i = 0; i < Count; i++) + { + Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn[i]); + if (!NT_SUCCESS(Status)) + { + for (j = 0; j < i; j++) + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn[j]); + } + return Status; + } + } + + PageDirectory = MmCreateHyperspaceMapping(Pfn[0]); + + memcpy(PageDirectory + ADDR_TO_PDE_OFFSET(MmSystemRangeStart), + MmGlobalKernelPageDirectory + ADDR_TO_PDE_OFFSET(MmSystemRangeStart), + (1024 - ADDR_TO_PDE_OFFSET(MmSystemRangeStart)) * sizeof(ULONG)); + + DPRINT("Addr %x\n",ADDR_TO_PDE_OFFSET(PAGETABLE_MAP)); + PageDirectory[ADDR_TO_PDE_OFFSET(PAGETABLE_MAP)] = PFN_TO_PTE(Pfn[0]) | PA_PRESENT | PA_READWRITE; + PageDirectory[ADDR_TO_PDE_OFFSET(HYPERSPACE)] = PFN_TO_PTE(Pfn[1]) | PA_PRESENT | PA_READWRITE; + + MmDeleteHyperspaceMapping(PageDirectory); + + DirectoryTableBase->QuadPart = PFN_TO_PTE(Pfn[0]); + DPRINT("Finished MmCopyMmInfo(): %I64x\n", DirectoryTableBase->QuadPart); + return(STATUS_SUCCESS); +} + +VOID +NTAPI +MmDeletePageTable(PEPROCESS Process, PVOID Address) +{ + PEPROCESS CurrentProcess = PsGetCurrentProcess(); + + if (Process != NULL && Process != CurrentProcess) + { + KeAttachProcess(&Process->Pcb); + } + + *(ADDR_TO_PDE(Address)) = 0; + MiFlushTlb(ADDR_TO_PDE(Address), ADDR_TO_PTE(Address)); + + if (Address >= MmSystemRangeStart) + { + KEBUGCHECK(0); + } + if (Process != NULL && Process != CurrentProcess) + { + KeDetachProcess(); + } +} + +VOID +NTAPI +MmFreePageTable(PEPROCESS Process, PVOID Address) +{ + PEPROCESS CurrentProcess = PsGetCurrentProcess(); + ULONG i; + PFN_TYPE Pfn; + + DPRINT("ProcessId %d, Address %x\n", Process->UniqueProcessId, Address); + if (Process != NULL && Process != CurrentProcess) + { + KeAttachProcess(&Process->Pcb); + } + + PULONG PageTable; + PageTable = (PULONG)PAGE_ROUND_DOWN((PVOID)ADDR_TO_PTE(Address)); + for (i = 0; i < 1024; i++) + { + if (PageTable[i] != 0) + { + DbgPrint("Page table entry not clear at %x/%x (is %x)\n", + ((ULONG)Address / (4*1024*1024)), i, PageTable[i]); + KEBUGCHECK(0); + } + } + Pfn = PTE_TO_PFN(*(ADDR_TO_PDE(Address))); + *(ADDR_TO_PDE(Address)) = 0; + MiFlushTlb(ADDR_TO_PDE(Address), ADDR_TO_PTE(Address)); + + if (Address >= MmSystemRangeStart) + { + // MmGlobalKernelPageDirectory[ADDR_TO_PDE_OFFSET(Address)] = 0; + KEBUGCHECK(0); + } + else + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + } + if (Process != NULL && Process != CurrentProcess) + { + KeDetachProcess(); + } +} + +static PULONG +MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create) +{ + ULONG PdeOffset = ADDR_TO_PDE_OFFSET(Address); + NTSTATUS Status; + PFN_TYPE Pfn; + ULONG Entry; + PULONG Pt, PageDir; + + if (Address < MmSystemRangeStart && Process && Process != PsGetCurrentProcess()) + { + PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase.QuadPart)); + if (PageDir == NULL) + { + KEBUGCHECK(0); + } + if (0 == InterlockedCompareExchangeUL(&PageDir[PdeOffset], 0, 0)) + { + if (Create == FALSE) + { + MmDeleteHyperspaceMapping(PageDir); + return NULL; + } + Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn); + if (!NT_SUCCESS(Status) || Pfn == 0) + { + KEBUGCHECK(0); + } + Entry = InterlockedCompareExchangeUL(&PageDir[PdeOffset], PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0); + if (Entry != 0) + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + Pfn = PTE_TO_PFN(Entry); + } + } + else + { + Pfn = PTE_TO_PFN(PageDir[PdeOffset]); + } + MmDeleteHyperspaceMapping(PageDir); + Pt = MmCreateHyperspaceMapping(Pfn); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + return Pt + ADDR_TO_PTE_OFFSET(Address); + } + PageDir = ADDR_TO_PDE(Address); + if (0 == InterlockedCompareExchangeUL(PageDir, 0, 0)) + { + if (Address >= MmSystemRangeStart) + { + if (0 == InterlockedCompareExchangeUL(&MmGlobalKernelPageDirectory[PdeOffset], 0, 0)) + { + if (Create == FALSE) + { + return NULL; + } + Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn); + if (!NT_SUCCESS(Status) || Pfn == 0) + { + KEBUGCHECK(0); + } + Entry = PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE; + if(0 != InterlockedCompareExchangeUL(&MmGlobalKernelPageDirectory[PdeOffset], Entry, 0)) + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + } + } + (void)InterlockedExchangeUL(PageDir, MmGlobalKernelPageDirectory[PdeOffset]); + } + else + { + if (Create == FALSE) + { + return NULL; + } + Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn); + if (!NT_SUCCESS(Status) || Pfn == 0) + { + KEBUGCHECK(0); + } + Entry = InterlockedCompareExchangeUL(PageDir, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0); + if (Entry != 0) + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + } + } + } + return (PULONG)ADDR_TO_PTE(Address); +} + +BOOLEAN MmUnmapPageTable(PULONG Pt) +{ + if (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024) + { + return TRUE; + } + + if (Pt) + { + MmDeleteHyperspaceMapping((PVOID)PAGE_ROUND_DOWN(Pt)); + } + return FALSE; +} + +static ULONG MmGetPageEntryForProcess(PEPROCESS Process, PVOID Address) +{ + ULONG Pte; + PULONG Pt; + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + if (Pt) + { + Pte = *Pt; + MmUnmapPageTable(Pt); + return Pte; + } + return 0; +} + +PFN_TYPE +NTAPI +MmGetPfnForProcess(PEPROCESS Process, + PVOID Address) +{ + ULONG Entry; + Entry = MmGetPageEntryForProcess(Process, Address); + return(PTE_TO_PFN(Entry)); +} + +VOID +NTAPI +MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_TYPE Page) +/* + * FUNCTION: Delete a virtual mapping + */ +{ + BOOLEAN WasValid; + ULONG Pte; + PULONG Pt; + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + /* + * Atomically disable the present bit and get the old value. + */ + do + { + Pte = *Pt; + } while (Pte != InterlockedCompareExchangeUL(Pt, Pte & ~PA_PRESENT, Pte)); + + MiFlushTlb(Pt, Address); + WasValid = (PAGE_MASK(Pte) != 0); + if (!WasValid) + { + KEBUGCHECK(0); + } + + /* + * Return some information to the caller + */ + if (WasDirty != NULL) + { + *WasDirty = Pte & PA_DIRTY; + } + if (Page != NULL) + { + *Page = PTE_TO_PFN(Pte); + } +} + +VOID +NTAPI +MmRawDeleteVirtualMapping(PVOID Address) +{ + PULONG Pt; + + Pt = MmGetPageTableForProcess(NULL, Address, FALSE); + if (Pt && *Pt) + { + /* + * Set the entry to zero + */ + (void)InterlockedExchangeUL(Pt, 0); + MiFlushTlb(Pt, Address); + } +} + +VOID +NTAPI +MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN FreePage, + BOOLEAN* WasDirty, PPFN_TYPE Page) +/* + * FUNCTION: Delete a virtual mapping + */ +{ + BOOLEAN WasValid = FALSE; + PFN_TYPE Pfn; + + DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n", + Process, Address, FreePage, WasDirty, Page); + + ULONG Pte; + PULONG Pt; + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + + if (Pt == NULL) + { + if (WasDirty != NULL) + { + *WasDirty = FALSE; + } + if (Page != NULL) + { + *Page = 0; + } + return; + } + + /* + * Atomically set the entry to zero and get the old value. + */ + Pte = InterlockedExchangeUL(Pt, 0); + + MiFlushTlb(Pt, Address); + + WasValid = (PAGE_MASK(Pte) != 0); + if (WasValid) + { + Pfn = PTE_TO_PFN(Pte); + MmMarkPageUnmapped(Pfn); + } + else + { + Pfn = 0; + } + + if (FreePage && WasValid) + { + MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn); + } + + /* + * Return some information to the caller + */ + if (WasDirty != NULL) + { + *WasDirty = Pte & PA_DIRTY ? TRUE : FALSE; + } + if (Page != NULL) + { + *Page = Pfn; + } + /* + * Decrement the reference count for this page table. + */ + if (Process != NULL && WasValid && + ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable != NULL && + Address < MmSystemRangeStart) + { + PUSHORT Ptrc; + ULONG Idx; + + Ptrc = ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable; + Idx = ADDR_TO_PAGE_TABLE(Address); + + Ptrc[Idx]--; + if (Ptrc[Idx] == 0) + { + MmFreePageTable(Process, Address); + } + } +} + +VOID +NTAPI +MmDeletePageFileMapping(PEPROCESS Process, PVOID Address, + SWAPENTRY* SwapEntry) +/* + * FUNCTION: Delete a virtual mapping + */ +{ + ULONG Pte; + PULONG Pt; + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + + if (Pt == NULL) + { + *SwapEntry = 0; + return; + } + + /* + * Atomically set the entry to zero and get the old value. + */ + Pte = InterlockedExchangeUL(Pt, 0); + + MiFlushTlb(Pt, Address); + + /* + * Decrement the reference count for this page table. + */ + if (Process != NULL && Pte && + ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable != NULL && + Address < MmSystemRangeStart) + { + PUSHORT Ptrc; + + Ptrc = ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable; + + Ptrc[ADDR_TO_PAGE_TABLE(Address)]--; + if (Ptrc[ADDR_TO_PAGE_TABLE(Address)] == 0) + { + MmFreePageTable(Process, Address); + } + } + + /* + * Return some information to the caller + */ + *SwapEntry = Pte >> 1; +} + +BOOLEAN +Mmi386MakeKernelPageTableGlobal(PVOID PAddress) +{ + PULONG Pt, Pde; + Pde = ADDR_TO_PDE(PAddress); + if (*Pde == 0) + { + Pt = MmGetPageTableForProcess(NULL, PAddress, FALSE); +#if 0 + /* Non existing mappings are not cached within the tlb. We must not invalidate this entry */ + FLASH_TLB_ONE(PAddress); +#endif + if (Pt != NULL) + { + return TRUE; + } + } + return(FALSE); +} + +BOOLEAN +NTAPI +MmIsDirtyPage(PEPROCESS Process, PVOID Address) +{ + return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : FALSE; +} + +BOOLEAN +NTAPI +MmIsAccessedAndResetAccessPage(PEPROCESS Process, PVOID Address) +{ + PULONG Pt; + ULONG Pte; + + if (Address < MmSystemRangeStart && Process == NULL) + { + DPRINT1("MmIsAccessedAndResetAccessPage is called for user space without a process.\n"); + KEBUGCHECK(0); + } + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + + do + { + Pte = *Pt; + } while (Pte != InterlockedCompareExchangeUL(Pt, Pte & ~PA_ACCESSED, Pte)); + + if (Pte & PA_ACCESSED) + { + MiFlushTlb(Pt, Address); + return TRUE; + } + else + { + MmUnmapPageTable(Pt); + return FALSE; + } +} + +VOID +NTAPI +MmSetCleanPage(PEPROCESS Process, PVOID Address) +{ + PULONG Pt; + ULONG Pte; + + if (Address < MmSystemRangeStart && Process == NULL) + { + DPRINT1("MmSetCleanPage is called for user space without a process.\n"); + KEBUGCHECK(0); + } + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + + if (Pt == NULL) + { + KEBUGCHECK(0); + } + + do + { + Pte = *Pt; + } while (Pte != InterlockedCompareExchangeUL(Pt, Pte & ~PA_DIRTY, Pte)); + + if (Pte & PA_DIRTY) + { + MiFlushTlb(Pt, Address); + } + else + { + MmUnmapPageTable(Pt); + } +} + +VOID +NTAPI +MmSetDirtyPage(PEPROCESS Process, PVOID Address) +{ + PULONG Pt; + ULONG Pte; + + if (Address < MmSystemRangeStart && Process == NULL) + { + DPRINT1("MmSetDirtyPage is called for user space without a process.\n"); + KEBUGCHECK(0); + } + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + + do + { + Pte = *Pt; + } while (Pte != InterlockedCompareExchangeUL(Pt, Pte | PA_DIRTY, Pte)); + if (!(Pte & PA_DIRTY)) + { + MiFlushTlb(Pt, Address); + } + else + { + MmUnmapPageTable(Pt); + } +} + +VOID +NTAPI +MmEnableVirtualMapping(PEPROCESS Process, PVOID Address) +{ + PULONG Pt; + ULONG Pte; + + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + + do + { + Pte = *Pt; + } while (Pte != InterlockedCompareExchangeUL(Pt, Pte | PA_PRESENT, Pte)); + if (!(Pte & PA_PRESENT)) + { + MiFlushTlb(Pt, Address); + } + else + { + MmUnmapPageTable(Pt); + } +} + +BOOLEAN +NTAPI +MmIsPagePresent(PEPROCESS Process, PVOID Address) +{ + return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT ? TRUE : FALSE; +} + +BOOLEAN +NTAPI +MmIsPageSwapEntry(PEPROCESS Process, PVOID Address) +{ + ULONG Entry; + Entry = MmGetPageEntryForProcess(Process, Address); + return !(Entry & PA_PRESENT) && Entry != 0 ? TRUE : FALSE; +} + +NTSTATUS +NTAPI +MmCreateVirtualMappingForKernel(PVOID Address, + ULONG flProtect, + PPFN_TYPE Pages, + ULONG PageCount) +{ + ULONG Attributes; + ULONG i; + PVOID Addr; + ULONG PdeOffset, oldPdeOffset; + BOOLEAN NoExecute = FALSE; + PULONG Pt; + ULONG Pte; + + DPRINT("MmCreateVirtualMappingForKernel(%x, %x, %x, %d)\n", + Address, flProtect, Pages, PageCount); + + if (Address < MmSystemRangeStart) + { + DPRINT1("MmCreateVirtualMappingForKernel is called for user space\n"); + KEBUGCHECK(0); + } + + Attributes = ProtectToPTE(flProtect); + if (Attributes & 0x80000000) + { + NoExecute = TRUE; + } + Attributes &= 0xfff; + + Addr = Address; + + oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr); + Pt = MmGetPageTableForProcess(NULL, Addr, TRUE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + Pt--; + + for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE)) + { + if (!(Attributes & PA_PRESENT) && Pages[i] != 0) + { + DPRINT1("Setting physical address but not allowing access at address " + "0x%.8X with attributes %x/%x.\n", + Addr, Attributes, flProtect); + KEBUGCHECK(0); + } + + PdeOffset = ADDR_TO_PDE_OFFSET(Addr); + if (oldPdeOffset != PdeOffset) + { + Pt = MmGetPageTableForProcess(NULL, Addr, TRUE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + } + else + { + Pt++; + } + oldPdeOffset = PdeOffset; + + Pte = *Pt; + if (Pte != 0) + { + KEBUGCHECK(0); + } + (void)InterlockedExchangeUL(Pt, PFN_TO_PTE(Pages[i]) | Attributes); + } + + return(STATUS_SUCCESS); +} + +NTSTATUS +NTAPI +MmCreatePageFileMapping(PEPROCESS Process, + PVOID Address, + SWAPENTRY SwapEntry) +{ + PULONG Pt; + ULONG Pte; + + if (Process == NULL && Address < MmSystemRangeStart) + { + DPRINT1("No process\n"); + KEBUGCHECK(0); + } + if (Process != NULL && Address >= MmSystemRangeStart) + { + DPRINT1("Setting kernel address with process context\n"); + KEBUGCHECK(0); + } + if (SwapEntry & (1 << 31)) + { + KEBUGCHECK(0); + } + + Pt = MmGetPageTableForProcess(Process, Address, TRUE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + Pte = *Pt; + if (PAGE_MASK((Pte)) != 0) + { + MmMarkPageUnmapped(PTE_TO_PFN((Pte))); + } + (void)InterlockedExchangeUL(Pt, SwapEntry << 1); + if (Pte != 0) + { + MiFlushTlb(Pt, Address); + } + else + { + MmUnmapPageTable(Pt); + } + + if (Process != NULL && + ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable != NULL && + Address < MmSystemRangeStart) + { + PUSHORT Ptrc; + ULONG Idx; + + Ptrc = ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable; + Idx = ADDR_TO_PAGE_TABLE(Address); + Ptrc[Idx]++; + } + return(STATUS_SUCCESS); +} + + +NTSTATUS +NTAPI +MmCreateVirtualMappingUnsafe(PEPROCESS Process, + PVOID Address, + ULONG flProtect, + PPFN_TYPE Pages, + ULONG PageCount) +{ + ULONG Attributes; + PVOID Addr; + ULONG i; + ULONG oldPdeOffset, PdeOffset; + BOOLEAN NoExecute = FALSE; + PULONG Pt = NULL; + ULONG Pte; + + DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n", + Process, Address, flProtect, Pages, *Pages, PageCount); + + if (Process == NULL) + { + if (Address < MmSystemRangeStart) + { + DPRINT1("No process\n"); + KEBUGCHECK(0); + } + if (PageCount > 0x10000 || + (ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000) + { + DPRINT1("Page count to large\n"); + KEBUGCHECK(0); + } + } + else + { + if (Address >= MmSystemRangeStart) + { + DPRINT1("Setting kernel address with process context\n"); + KEBUGCHECK(0); + } + if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE || + (ULONG_PTR) Address / PAGE_SIZE + PageCount > + (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE) + { + DPRINT1("Page Count to large\n"); + KEBUGCHECK(0); + } + } + + Attributes = ProtectToPTE(flProtect); + if (Attributes & 0x80000000) + { + NoExecute = TRUE; + } + Attributes &= 0xfff; + if (Address >= MmSystemRangeStart) + { + Attributes &= ~PA_USER; + } + else + { + Attributes |= PA_USER; + } + + Addr = Address; + + oldPdeOffset = ADDR_TO_PDE_OFFSET(Addr) + 1; + for (i = 0; i < PageCount; i++, Addr = (PVOID)((ULONG_PTR)Addr + PAGE_SIZE)) + { + if (!(Attributes & PA_PRESENT) && Pages[i] != 0) + { + DPRINT1("Setting physical address but not allowing access at address " + "0x%.8X with attributes %x/%x.\n", + Addr, Attributes, flProtect); + KEBUGCHECK(0); + } + PdeOffset = ADDR_TO_PDE_OFFSET(Addr); + if (oldPdeOffset != PdeOffset) + { + MmUnmapPageTable(Pt); + Pt = MmGetPageTableForProcess(Process, Addr, TRUE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + } + else + { + Pt++; + } + oldPdeOffset = PdeOffset; + + Pte = *Pt; + MmMarkPageMapped(Pages[i]); + if (PAGE_MASK((Pte)) != 0 && !((Pte) & PA_PRESENT)) + { + KEBUGCHECK(0); + } + if (PAGE_MASK((Pte)) != 0) + { + MmMarkPageUnmapped(PTE_TO_PFN((Pte))); + } + (void)InterlockedExchangeUL(Pt, PFN_TO_PTE(Pages[i]) | Attributes); + if (Address < MmSystemRangeStart && + ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable != NULL && + Attributes & PA_PRESENT) + { + PUSHORT Ptrc; + + Ptrc = ((PMADDRESS_SPACE)&Process->VadRoot)->PageTableRefCountTable; + + Ptrc[ADDR_TO_PAGE_TABLE(Addr)]++; + } + if (Pte != 0) + { + if (Address > MmSystemRangeStart || + (Pt >= (PULONG)PAGETABLE_MAP && Pt < (PULONG)PAGETABLE_MAP + 1024*1024)) + { + MiFlushTlb(Pt, Address); + } + } + } + if (Addr > Address) + { + MmUnmapPageTable(Pt); + } + return(STATUS_SUCCESS); +} + +NTSTATUS +NTAPI +MmCreateVirtualMapping(PEPROCESS Process, + PVOID Address, + ULONG flProtect, + PPFN_TYPE Pages, + ULONG PageCount) +{ + ULONG i; + + for (i = 0; i < PageCount; i++) + { + if (!MmIsUsablePage(Pages[i])) + { + DPRINT1("Page at address %x not usable\n", PFN_TO_PTE(Pages[i])); + KEBUGCHECK(0); + } + } + + return(MmCreateVirtualMappingUnsafe(Process, + Address, + flProtect, + Pages, + PageCount)); +} + +ULONG +NTAPI +MmGetPageProtect(PEPROCESS Process, PVOID Address) +{ + ULONG Entry; + ULONG Protect; + + Entry = MmGetPageEntryForProcess(Process, Address); + + if (!(Entry & PA_PRESENT)) + { + Protect = PAGE_NOACCESS; + } + else + { + if (Entry & PA_READWRITE) + { + Protect = PAGE_READWRITE; + } + else + { + Protect = PAGE_EXECUTE_READ; + } + if (Entry & PA_CD) + { + Protect |= PAGE_NOCACHE; + } + if (Entry & PA_WT) + { + Protect |= PAGE_WRITETHROUGH; + } + if (!(Entry & PA_USER)) + { + Protect |= PAGE_SYSTEM; + } + + } + return(Protect); +} + +VOID +NTAPI +MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect) +{ + ULONG Attributes = 0; + + DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n", + Process, Address, flProtect); + + Attributes = ProtectToPTE(flProtect); + +#if 0 + Pt = MmGetPageTableForProcess(Process, Address, FALSE); + if (Pt == NULL) + { + KEBUGCHECK(0); + } + InterlockedExchange((PLONG)Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY))); + MiFlushTlb(Pt, Address); +#endif +} + +/* + * @implemented + */ +PHYSICAL_ADDRESS STDCALL +MmGetPhysicalAddress(PVOID vaddr) +/* + * FUNCTION: Returns the physical address corresponding to a virtual address + */ +{ + PHYSICAL_ADDRESS p; + ULONG Pte; + + DPRINT("MmGetPhysicalAddress(vaddr %x)\n", vaddr); + Pte = MmGetPageEntryForProcess(NULL, vaddr); + if (Pte != 0 && Pte & PA_PRESENT) + { + p.QuadPart = PAGE_MASK(Pte); + p.u.LowPart |= (ULONG_PTR)vaddr & (PAGE_SIZE - 1); + } + else + { + p.QuadPart = 0; + } + return p; +} + +PVOID +NTAPI +MmCreateHyperspaceMapping(PFN_TYPE Page) +{ + PVOID Address; + ULONG i; + ULONG Entry; + PULONG Pte; + Entry = PFN_TO_PTE(Page) | PA_PRESENT | PA_READWRITE; + Pte = ADDR_TO_PTE(HYPERSPACE) + Page % 1024; + if (Page & 1024) + { + for (i = Page % 1024; i < 1024; i++, Pte++) + { + if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0)) + { + break; + } + } + if (i >= 1024) + { + Pte = ADDR_TO_PTE(HYPERSPACE); + for (i = 0; i < Page % 1024; i++, Pte++) + { + if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0)) + { + break; + } + } + if (i >= Page % 1024) + { + KEBUGCHECK(0); + } + } + } + else + { + for (i = Page % 1024; (LONG)i >= 0; i--, Pte--) + { + if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0)) + { + break; + } + } + if ((LONG)i < 0) + { + Pte = ADDR_TO_PTE(HYPERSPACE) + 1023; + for (i = 1023; i > Page % 1024; i--, Pte--) + { + if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0)) + { + break; + } + } + if (i <= Page % 1024) + { + KEBUGCHECK(0); + } + } + } + Address = (PVOID)((ULONG_PTR)HYPERSPACE + i * PAGE_SIZE); + FLUSH_TLB_ONE(Address); + return Address; +} + +PFN_TYPE +NTAPI +MmChangeHyperspaceMapping(PVOID Address, PFN_TYPE NewPage) +{ + PFN_TYPE Pfn; + ASSERT (IS_HYPERSPACE(Address)); + ULONG Entry; + Entry = InterlockedExchange((PLONG)ADDR_TO_PTE(Address), PFN_TO_PTE(NewPage) | PA_PRESENT | PA_READWRITE); + Pfn = PTE_TO_PFN(Entry); + FLUSH_TLB_ONE(Address); + return Pfn; +} + +PFN_TYPE +NTAPI +MmDeleteHyperspaceMapping(PVOID Address) +{ + PFN_TYPE Pfn; + ASSERT (IS_HYPERSPACE(Address)); + ULONG Entry; + Entry = InterlockedExchange((PLONG)ADDR_TO_PTE(Address), 0); + Pfn = PTE_TO_PFN(Entry); + FLUSH_TLB_ONE(Address); + return Pfn; +} + +VOID +NTAPI +MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size) +{ + ULONG StartOffset, EndOffset, Offset; + PULONG Pde; + + if (Address < MmSystemRangeStart) + { + KEBUGCHECK(0); + } + StartOffset = ADDR_TO_PDE_OFFSET(Address); + EndOffset = ADDR_TO_PDE_OFFSET((PVOID)((ULONG_PTR)Address + Size)); + + if (Process != NULL && Process != PsGetCurrentProcess()) + { + Pde = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase.u.LowPart)); + } + else + { + Pde = (PULONG)PAGEDIRECTORY_MAP; + } + for (Offset = StartOffset; Offset <= EndOffset; Offset++) + { + if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP)) + { + (void)InterlockedCompareExchangeUL(&Pde[Offset], MmGlobalKernelPageDirectory[Offset], 0); + } + } + if (Pde != (PULONG)PAGEDIRECTORY_MAP) + { + MmDeleteHyperspaceMapping(Pde); + } +} + +VOID +INIT_FUNCTION +NTAPI +MmInitGlobalKernelPageDirectory(VOID) +{ + ULONG i; + PULONG CurrentPageDirectory = (PULONG)PAGEDIRECTORY_MAP; + + DPRINT("MmInitGlobalKernelPageDirectory()\n"); + + for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++) + { + if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) && + i != ADDR_TO_PDE_OFFSET(HYPERSPACE) && + 0 == MmGlobalKernelPageDirectory[i] && 0 != CurrentPageDirectory[i]) + { + MmGlobalKernelPageDirectory[i] = CurrentPageDirectory[i]; + } + } +} + +ULONG +NTAPI +MiGetUserPageDirectoryCount(VOID) +{ + return ADDR_TO_PDE_OFFSET(MmSystemRangeStart); +} + +VOID +INIT_FUNCTION +NTAPI +MiInitPageDirectoryMap(VOID) +{ + MEMORY_AREA* kernel_map_desc = NULL; + MEMORY_AREA* hyperspace_desc = NULL; + PHYSICAL_ADDRESS BoundaryAddressMultiple; + PVOID BaseAddress; + NTSTATUS Status; + + DPRINT("MiInitPageDirectoryMap()\n"); + + BoundaryAddressMultiple.QuadPart = 0; + BaseAddress = (PVOID)PAGETABLE_MAP; + Status = MmCreateMemoryArea(MmGetKernelAddressSpace(), + MEMORY_AREA_SYSTEM, + &BaseAddress, + 0x400000, + PAGE_READWRITE, + &kernel_map_desc, + TRUE, + 0, + BoundaryAddressMultiple); + if (!NT_SUCCESS(Status)) + { + KEBUGCHECK(0); + } + BaseAddress = (PVOID)HYPERSPACE; + Status = MmCreateMemoryArea(MmGetKernelAddressSpace(), + MEMORY_AREA_SYSTEM, + &BaseAddress, + 0x400000, + PAGE_READWRITE, + &hyperspace_desc, + TRUE, + 0, + BoundaryAddressMultiple); + if (!NT_SUCCESS(Status)) + { + KEBUGCHECK(0); + } +} + +/* EOF */