reactos/ntoskrnl/mm/i386/page.c
Jérôme Gardou c16ad873a6 sync with trunk (r46275)
svn path=/branches/reactos-yarotows/; revision=46279
2010-03-19 21:09:21 +00:00

1013 lines
24 KiB
C

/*
* 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)
*/
/* INCLUDES ***************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
#pragma alloc_text(INIT, MiInitPageDirectoryMap)
#endif
/* GLOBALS *****************************************************************/
#define PA_BIT_PRESENT (0)
#define PA_BIT_READWRITE (1)
#define PA_BIT_USER (2)
#define PA_BIT_WT (3)
#define PA_BIT_CD (4)
#define PA_BIT_ACCESSED (5)
#define PA_BIT_DIRTY (6)
#define PA_BIT_GLOBAL (8)
#define PA_PRESENT (1 << PA_BIT_PRESENT)
#define PA_READWRITE (1 << PA_BIT_READWRITE)
#define PA_USER (1 << PA_BIT_USER)
#define PA_DIRTY (1 << PA_BIT_DIRTY)
#define PA_WT (1 << PA_BIT_WT)
#define PA_CD (1 << PA_BIT_CD)
#define PA_ACCESSED (1 << PA_BIT_ACCESSED)
#define PA_GLOBAL (1 << PA_BIT_GLOBAL)
#define HYPERSPACE (0xc0400000)
#define IS_HYPERSPACE(v) (((ULONG)(v) >= HYPERSPACE && (ULONG)(v) < HYPERSPACE + 0x400000))
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
MiFlushTlb(PULONG Pt, PVOID Address)
{
if ((Pt && MmUnmapPageTable(Pt)) || Address >= MmSystemRangeStart)
{
KeInvalidateTlbEntry(Address);
}
}
static ULONG
ProtectToPTE(ULONG flProtect)
{
ULONG Attributes = 0;
if (flProtect & (PAGE_NOACCESS|PAGE_GUARD))
{
Attributes = 0;
}
else if (flProtect & PAGE_IS_WRITABLE)
{
Attributes = PA_PRESENT | PA_READWRITE;
}
else if (flProtect & (PAGE_IS_READABLE | PAGE_IS_EXECUTABLE))
{
Attributes = PA_PRESENT;
}
else
{
DPRINT1("Unknown main protection type.\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (flProtect & PAGE_SYSTEM)
{
}
else
{
Attributes = Attributes | PA_USER;
}
if (flProtect & PAGE_NOCACHE)
{
Attributes = Attributes | PA_CD;
}
if (flProtect & PAGE_WRITETHROUGH)
{
Attributes = Attributes | PA_WT;
}
return(Attributes);
}
NTSTATUS
NTAPI
Mmi386ReleaseMmInfo(PEPROCESS Process)
{
PUSHORT LdtDescriptor;
ULONG LdtBase;
PULONG PageDir;
ULONG i;
DPRINT("Mmi386ReleaseMmInfo(Process %x)\n",Process);
LdtDescriptor = (PUSHORT) &Process->Pcb.LdtDescriptor;
LdtBase = LdtDescriptor[1] |
((LdtDescriptor[2] & 0xff) << 16) |
((LdtDescriptor[3] & ~0xff) << 16);
DPRINT("LdtBase: %x\n", LdtBase);
if (LdtBase)
{
ExFreePool((PVOID) LdtBase);
}
PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
for (i = 0; i < ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i++)
{
if (PageDir[i] != 0)
{
MiZeroPage(PTE_TO_PFN(PageDir[i]));
MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(PageDir[i]));
}
}
MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(PageDir[ADDR_TO_PDE_OFFSET(HYPERSPACE)]));
MmDeleteHyperspaceMapping(PageDir);
MmReleasePageMemoryConsumer(MC_NPPOOL, PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
Process->Pcb.DirectoryTableBase[0] = 0;
Process->Pcb.DirectoryTableBase[1] = 0;
DPRINT("Finished Mmi386ReleaseMmInfo()\n");
return(STATUS_SUCCESS);
}
NTSTATUS
NTAPI
MmInitializeHandBuiltProcess(IN PEPROCESS Process,
IN PULONG DirectoryTableBase)
{
/* Share the directory base with the idle process */
DirectoryTableBase[0] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[0];
DirectoryTableBase[1] = PsGetCurrentProcess()->Pcb.DirectoryTableBase[1];
/* Initialize the Addresss Space */
KeInitializeGuardedMutex(&Process->AddressCreationLock);
Process->Vm.WorkingSetExpansionLinks.Flink = NULL;
ASSERT(Process->VadRoot.NumberGenericTableElements == 0);
Process->VadRoot.BalancedRoot.u1.Parent = &Process->VadRoot.BalancedRoot;
/* The process now has an address space */
Process->HasAddressSpace = TRUE;
return STATUS_SUCCESS;
}
BOOLEAN
NTAPI
MmCreateProcessAddressSpace(IN ULONG MinWs,
IN PEPROCESS Process,
IN PULONG DirectoryTableBase)
{
NTSTATUS Status;
ULONG i, j;
PFN_TYPE Pfn[2];
PULONG PageDirectory;
DPRINT("MmCopyMmInfo(Src %x, Dest %x)\n", MinWs, Process);
for (i = 0; i < 2; i++)
{
Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn[i]);
if (!NT_SUCCESS(Status))
{
for (j = 0; j < i; j++)
{
MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn[j]);
}
return FALSE;
}
}
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[0] = PFN_TO_PTE(Pfn[0]);
DirectoryTableBase[1] = 0;
DPRINT("Finished MmCopyMmInfo(): 0x%x\n", DirectoryTableBase[0]);
return TRUE;
}
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[0]));
if (PageDir == NULL)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
if (0 == InterlockedCompareExchangePte(&PageDir[PdeOffset], 0, 0))
{
if (Create == FALSE)
{
MmDeleteHyperspaceMapping(PageDir);
return NULL;
}
Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
if (!NT_SUCCESS(Status) || Pfn == 0)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
Entry = InterlockedCompareExchangePte(&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(MEMORY_MANAGEMENT);
}
return Pt + ADDR_TO_PTE_OFFSET(Address);
}
PageDir = (PULONG)MiAddressToPde(Address);
if (0 == InterlockedCompareExchangePte(PageDir, 0, 0))
{
if (Address >= MmSystemRangeStart)
{
if (0 == InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], 0, 0))
{
if (Create == FALSE)
{
return NULL;
}
Status = MmRequestPageMemoryConsumer(MC_SYSTEM, FALSE, &Pfn);
if (!NT_SUCCESS(Status) || Pfn == 0)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
Entry = PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE;
if(0 != InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], Entry, 0))
{
MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
}
InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
RtlZeroMemory(MiPteToAddress(PageDir), PAGE_SIZE);
return (PULONG)MiAddressToPte(Address);
}
InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
}
else
{
if (Create == FALSE)
{
return NULL;
}
Status = MmRequestPageMemoryConsumer(MC_NPPOOL, FALSE, &Pfn);
if (!NT_SUCCESS(Status) || Pfn == 0)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
Entry = InterlockedCompareExchangePte(PageDir, PFN_TO_PTE(Pfn) | PA_PRESENT | PA_READWRITE | PA_USER, 0);
if (Entry != 0)
{
MmReleasePageMemoryConsumer(MC_NPPOOL, Pfn);
}
}
}
return (PULONG)MiAddressToPte(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);
if (!(Entry & PA_PRESENT))
{
return 0;
}
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(MEMORY_MANAGEMENT);
}
/*
* Atomically disable the present bit and get the old value.
*/
do
{
Pte = *Pt;
} while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_PRESENT, Pte));
MiFlushTlb(Pt, Address);
WasValid = (PAGE_MASK(Pte) != 0);
if (!WasValid)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
/*
* 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
*/
InterlockedExchangePte(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;
ULONG Pte;
PULONG Pt;
DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
Process, Address, FreePage, WasDirty, Page);
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 = InterlockedExchangePte(Pt, 0);
MiFlushTlb(Pt, Address);
WasValid = (PAGE_MASK(Pte) != 0);
if (WasValid)
{
Pfn = PTE_TO_PFN(Pte);
}
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;
}
}
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 = InterlockedExchangePte(Pt, 0);
MiFlushTlb(Pt, Address);
/*
* Return some information to the caller
*/
*SwapEntry = Pte >> 1;
}
BOOLEAN
Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
{
PULONG Pt, Pde;
Pde = (PULONG)MiAddressToPde(PAddress);
if (*Pde == 0)
{
Pt = MmGetPageTableForProcess(NULL, PAddress, FALSE);
if (Pt != NULL)
{
return TRUE;
}
}
return(FALSE);
}
BOOLEAN
NTAPI
MmIsDirtyPage(PEPROCESS Process, PVOID Address)
{
return MmGetPageEntryForProcess(Process, Address) & PA_DIRTY ? TRUE : 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(MEMORY_MANAGEMENT);
}
Pt = MmGetPageTableForProcess(Process, Address, FALSE);
if (Pt == NULL)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
do
{
Pte = *Pt;
} while (Pte != InterlockedCompareExchangePte(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(MEMORY_MANAGEMENT);
}
Pt = MmGetPageTableForProcess(Process, Address, FALSE);
if (Pt == NULL)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
do
{
Pte = *Pt;
} while (Pte != InterlockedCompareExchangePte(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(MEMORY_MANAGEMENT);
}
do
{
Pte = *Pt;
} while (Pte != InterlockedCompareExchangePte(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;
}
BOOLEAN
NTAPI
MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
{
ULONG Entry;
Entry = MmGetPageEntryForProcess(Process, Address);
return !(Entry & PA_PRESENT) && (Entry & 0x800) && Entry != 0;
}
NTSTATUS
NTAPI
MmCreatePageFileMapping(PEPROCESS Process,
PVOID Address,
SWAPENTRY SwapEntry)
{
PULONG Pt;
ULONG Pte;
if (Process == NULL && Address < MmSystemRangeStart)
{
DPRINT1("No process\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (Process != NULL && Address >= MmSystemRangeStart)
{
DPRINT1("Setting kernel address with process context\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (SwapEntry & (1 << 31))
{
KeBugCheck(MEMORY_MANAGEMENT);
}
Pt = MmGetPageTableForProcess(Process, Address, TRUE);
if (Pt == NULL)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
Pte = *Pt;
InterlockedExchangePte(Pt, SwapEntry << 1);
if (Pte != 0)
{
MiFlushTlb(Pt, Address);
}
else
{
MmUnmapPageTable(Pt);
}
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;
PULONG Pt = NULL;
ULONG Pte;
BOOLEAN NoExecute = FALSE;
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(MEMORY_MANAGEMENT);
}
if (PageCount > 0x10000 ||
(ULONG_PTR) Address / PAGE_SIZE + PageCount > 0x100000)
{
DPRINT1("Page count too large\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
}
else
{
if (Address >= MmSystemRangeStart)
{
DPRINT1("Setting kernel address with process context\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
if (PageCount > (ULONG_PTR)MmSystemRangeStart / PAGE_SIZE ||
(ULONG_PTR) Address / PAGE_SIZE + PageCount >
(ULONG_PTR)MmSystemRangeStart / PAGE_SIZE)
{
DPRINT1("Page Count too large\n");
KeBugCheck(MEMORY_MANAGEMENT);
}
}
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(MEMORY_MANAGEMENT);
}
PdeOffset = ADDR_TO_PDE_OFFSET(Addr);
if (oldPdeOffset != PdeOffset)
{
MmUnmapPageTable(Pt);
Pt = MmGetPageTableForProcess(Process, Addr, TRUE);
if (Pt == NULL)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
}
else
{
Pt++;
}
oldPdeOffset = PdeOffset;
Pte = *Pt;
if (PAGE_MASK(Pte) != 0 && !(Pte & PA_PRESENT) && (Pte & 0x800))
{
DPRINT1("Bad PTE %lx\n", Pte);
KeBugCheck(MEMORY_MANAGEMENT);
}
InterlockedExchangePte(Pt, PFN_TO_PTE(Pages[i]) | Attributes);
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 (!MmIsPageInUse(Pages[i]))
{
DPRINT1("Page at address %x not in use\n", PFN_TO_PTE(Pages[i]));
KeBugCheck(MEMORY_MANAGEMENT);
}
}
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;
BOOLEAN NoExecute = FALSE;
PULONG Pt;
DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
Process, Address, flProtect);
Attributes = ProtectToPTE(flProtect);
if (Attributes & 0x80000000)
{
NoExecute = TRUE;
}
Attributes &= 0xfff;
if (Address >= MmSystemRangeStart)
{
Attributes &= ~PA_USER;
}
else
{
Attributes |= PA_USER;
}
Pt = MmGetPageTableForProcess(Process, Address, FALSE);
if (Pt == NULL)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
MiFlushTlb(Pt, Address);
}
/*
* @implemented
*/
PHYSICAL_ADDRESS NTAPI
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;
}
VOID
NTAPI
MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
{
ULONG StartOffset, EndOffset, Offset;
PULONG Pde;
//
// Check if the process isn't there anymore
// This is probably a bad sign, since it means the caller is setting cr3 to
// 0 or something...
//
if ((PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]) == 0) && (Process != PsGetCurrentProcess()))
{
DPRINT1("Process: %16s is dead: %p\n", Process->ImageFileName, Process->Pcb.DirectoryTableBase[0]);
ASSERT(FALSE);
return;
}
if (Address < MmSystemRangeStart)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
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[0]));
}
else
{
Pde = (PULONG)PAGEDIRECTORY_MAP;
}
for (Offset = StartOffset; Offset <= EndOffset; Offset++)
{
if (Offset != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP))
{
InterlockedCompareExchangePte(&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];
}
}
}
/* EOF */