mirror of
https://github.com/reactos/reactos.git
synced 2024-12-29 10:35:28 +00:00
ffb20d3330
- Adress -> Address - Currupted -> Corrupted 3rd-party files are not modified.
947 lines
26 KiB
C
947 lines
26 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/mm/i386/page.c
|
|
* PURPOSE: Low level memory management manipulation
|
|
*
|
|
* PROGRAMMERS: David Welch (welch@cwcom.net)
|
|
*/
|
|
|
|
/* INCLUDES ***************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include <mm/ARM3/miarm.h>
|
|
|
|
#ifndef _MI_PAGING_LEVELS
|
|
#error "Dude, fix your stuff before using this file"
|
|
#endif
|
|
|
|
/* GLOBALS *****************************************************************/
|
|
const
|
|
ULONG_PTR
|
|
MmProtectToPteMask[32] =
|
|
{
|
|
//
|
|
// These are the base MM_ protection flags
|
|
//
|
|
0,
|
|
PTE_READONLY | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
|
|
PTE_READWRITE | PTE_ENABLE_CACHE,
|
|
PTE_WRITECOPY | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
|
|
//
|
|
// These OR in the MM_NOCACHE flag
|
|
//
|
|
0,
|
|
PTE_READONLY | PTE_DISABLE_CACHE,
|
|
PTE_EXECUTE | PTE_DISABLE_CACHE,
|
|
PTE_EXECUTE_READ | PTE_DISABLE_CACHE,
|
|
PTE_READWRITE | PTE_DISABLE_CACHE,
|
|
PTE_WRITECOPY | PTE_DISABLE_CACHE,
|
|
PTE_EXECUTE_READWRITE | PTE_DISABLE_CACHE,
|
|
PTE_EXECUTE_WRITECOPY | PTE_DISABLE_CACHE,
|
|
//
|
|
// These OR in the MM_DECOMMIT flag, which doesn't seem supported on x86/64/ARM
|
|
//
|
|
0,
|
|
PTE_READONLY | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE_READ | PTE_ENABLE_CACHE,
|
|
PTE_READWRITE | PTE_ENABLE_CACHE,
|
|
PTE_WRITECOPY | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE_READWRITE | PTE_ENABLE_CACHE,
|
|
PTE_EXECUTE_WRITECOPY | PTE_ENABLE_CACHE,
|
|
//
|
|
// These OR in the MM_NOACCESS flag, which seems to enable WriteCombining?
|
|
//
|
|
0,
|
|
PTE_READONLY | PTE_WRITECOMBINED_CACHE,
|
|
PTE_EXECUTE | PTE_WRITECOMBINED_CACHE,
|
|
PTE_EXECUTE_READ | PTE_WRITECOMBINED_CACHE,
|
|
PTE_READWRITE | PTE_WRITECOMBINED_CACHE,
|
|
PTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
|
|
PTE_EXECUTE_READWRITE | PTE_WRITECOMBINED_CACHE,
|
|
PTE_EXECUTE_WRITECOPY | PTE_WRITECOMBINED_CACHE,
|
|
};
|
|
|
|
const
|
|
ULONG MmProtectToValue[32] =
|
|
{
|
|
PAGE_NOACCESS,
|
|
PAGE_READONLY,
|
|
PAGE_EXECUTE,
|
|
PAGE_EXECUTE_READ,
|
|
PAGE_READWRITE,
|
|
PAGE_WRITECOPY,
|
|
PAGE_EXECUTE_READWRITE,
|
|
PAGE_EXECUTE_WRITECOPY,
|
|
PAGE_NOACCESS,
|
|
PAGE_NOCACHE | PAGE_READONLY,
|
|
PAGE_NOCACHE | PAGE_EXECUTE,
|
|
PAGE_NOCACHE | PAGE_EXECUTE_READ,
|
|
PAGE_NOCACHE | PAGE_READWRITE,
|
|
PAGE_NOCACHE | PAGE_WRITECOPY,
|
|
PAGE_NOCACHE | PAGE_EXECUTE_READWRITE,
|
|
PAGE_NOCACHE | PAGE_EXECUTE_WRITECOPY,
|
|
PAGE_NOACCESS,
|
|
PAGE_GUARD | PAGE_READONLY,
|
|
PAGE_GUARD | PAGE_EXECUTE,
|
|
PAGE_GUARD | PAGE_EXECUTE_READ,
|
|
PAGE_GUARD | PAGE_READWRITE,
|
|
PAGE_GUARD | PAGE_WRITECOPY,
|
|
PAGE_GUARD | PAGE_EXECUTE_READWRITE,
|
|
PAGE_GUARD | PAGE_EXECUTE_WRITECOPY,
|
|
PAGE_NOACCESS,
|
|
PAGE_WRITECOMBINE | PAGE_READONLY,
|
|
PAGE_WRITECOMBINE | PAGE_EXECUTE,
|
|
PAGE_WRITECOMBINE | PAGE_EXECUTE_READ,
|
|
PAGE_WRITECOMBINE | PAGE_READWRITE,
|
|
PAGE_WRITECOMBINE | PAGE_WRITECOPY,
|
|
PAGE_WRITECOMBINE | PAGE_EXECUTE_READWRITE,
|
|
PAGE_WRITECOMBINE | PAGE_EXECUTE_WRITECOPY
|
|
};
|
|
|
|
/* FUNCTIONS ***************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiFillSystemPageDirectory(IN PVOID Base,
|
|
IN SIZE_T NumberOfBytes);
|
|
|
|
static
|
|
BOOLEAN
|
|
MiIsPageTablePresent(PVOID Address)
|
|
{
|
|
#if _MI_PAGING_LEVELS == 2
|
|
BOOLEAN Ret = MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] != 0;
|
|
|
|
/* Some sanity check while we're here */
|
|
ASSERT(Ret == (MiAddressToPde(Address)->u.Hard.Valid != 0));
|
|
|
|
return Ret;
|
|
#else
|
|
PMMPDE PointerPde;
|
|
PMMPPE PointerPpe;
|
|
#if _MI_PAGING_LEVELS == 4
|
|
PMMPXE PointerPxe;
|
|
#endif
|
|
PMMPFN Pfn;
|
|
|
|
/* Make sure we're locked */
|
|
ASSERT((PsGetCurrentThread()->OwnsProcessWorkingSetExclusive) || (PsGetCurrentThread()->OwnsProcessWorkingSetShared));
|
|
|
|
/* Must not hold the PFN lock! */
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
/* Check if PXE or PPE have references first. */
|
|
#if _MI_PAGING_LEVELS == 4
|
|
PointerPxe = MiAddressToPxe(Address);
|
|
if ((PointerPxe->u.Hard.Valid == 1) || (PointerPxe->u.Soft.Transition == 1))
|
|
{
|
|
Pfn = MiGetPfnEntry(PFN_FROM_PXE(PointerPxe));
|
|
if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
|
|
return FALSE;
|
|
}
|
|
else if (PointerPxe->u.Soft.UsedPageTableEntries == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (PointerPxe->u.Hard.Valid == 0)
|
|
{
|
|
MiMakeSystemAddressValid(MiPteToAddress(PointerPxe), PsGetCurrentProcess());
|
|
}
|
|
#endif
|
|
|
|
PointerPpe = MiAddressToPpe(Address);
|
|
if ((PointerPpe->u.Hard.Valid == 1) || (PointerPpe->u.Soft.Transition == 1))
|
|
{
|
|
Pfn = MiGetPfnEntry(PFN_FROM_PPE(PointerPpe));
|
|
if (Pfn->OriginalPte.u.Soft.UsedPageTableEntries == 0)
|
|
return FALSE;
|
|
}
|
|
else if (PointerPpe->u.Soft.UsedPageTableEntries == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (PointerPpe->u.Hard.Valid == 0)
|
|
{
|
|
MiMakeSystemAddressValid(MiPteToAddress(PointerPpe), PsGetCurrentProcess());
|
|
}
|
|
|
|
PointerPde = MiAddressToPde(Address);
|
|
if ((PointerPde->u.Hard.Valid == 0) && (PointerPde->u.Soft.Transition == 0))
|
|
{
|
|
return PointerPde->u.Soft.UsedPageTableEntries != 0;
|
|
}
|
|
|
|
/* This lies on the PFN */
|
|
Pfn = MiGetPfnEntry(PFN_FROM_PDE(PointerPde));
|
|
return Pfn->OriginalPte.u.Soft.UsedPageTableEntries != 0;
|
|
#endif
|
|
}
|
|
|
|
PFN_NUMBER
|
|
NTAPI
|
|
MmGetPfnForProcess(PEPROCESS Process,
|
|
PVOID Address)
|
|
{
|
|
PMMPTE PointerPte;
|
|
PFN_NUMBER Page;
|
|
|
|
/* Must be called for user mode only */
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Address < MmSystemRangeStart);
|
|
|
|
/* And for our process */
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
/* Lock for reading */
|
|
MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
if (!MiIsPageTablePresent(Address))
|
|
{
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
return 0;
|
|
}
|
|
|
|
/* Make sure we can read the PTE */
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
Page = PointerPte->u.Hard.Valid ? PFN_FROM_PTE(PointerPte) : 0;
|
|
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
return Page;
|
|
}
|
|
|
|
/**
|
|
* @brief Deletes the virtual mapping and optionally gives back the page & dirty bit.
|
|
*
|
|
* @param Process - The process this address belongs to, or NULL if system address.
|
|
* @param Address - The address to unmap.
|
|
* @param WasDirty - Optional param receiving the dirty bit of the PTE.
|
|
* @param Page - Optional param receiving the page number previously mapped to this address.
|
|
*
|
|
* @return Whether there was actually a page mapped at the given address.
|
|
*/
|
|
_Success_(return)
|
|
BOOLEAN
|
|
MmDeleteVirtualMappingEx(
|
|
_Inout_opt_ PEPROCESS Process,
|
|
_In_ PVOID Address,
|
|
_Out_opt_ BOOLEAN* WasDirty,
|
|
_Out_opt_ PPFN_NUMBER Page,
|
|
_In_ BOOLEAN IsPhysical)
|
|
{
|
|
PMMPTE PointerPte;
|
|
MMPTE OldPte;
|
|
BOOLEAN ValidPde;
|
|
|
|
OldPte.u.Long = 0;
|
|
|
|
DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n", Process, Address, WasDirty, Page);
|
|
|
|
ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
|
|
|
|
/* And we should be at low IRQL */
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
/* Make sure our PDE is valid, and that everything is going fine */
|
|
if (Process == NULL)
|
|
{
|
|
if (Address < MmSystemRangeStart)
|
|
{
|
|
DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
#if (_MI_PAGING_LEVELS == 2)
|
|
ValidPde = MiSynchronizeSystemPde(MiAddressToPde(Address));
|
|
#else
|
|
ValidPde = MiIsPdeForAddressValid(Address);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
|
|
{
|
|
DPRINT1("Process %p given for kernel-mode mapping at %p\n", Process, Address);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
/* Only for current process !!! */
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
ValidPde = MiIsPageTablePresent(Address);
|
|
if (ValidPde)
|
|
{
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
}
|
|
}
|
|
|
|
/* Get the PTE if we're having anything */
|
|
if (ValidPde)
|
|
{
|
|
PointerPte = MiAddressToPte(Address);
|
|
OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
|
|
|
|
KeInvalidateTlbEntry(Address);
|
|
|
|
if (OldPte.u.Long != 0)
|
|
{
|
|
/* It must have been present, or not a swap entry */
|
|
ASSERT(OldPte.u.Hard.Valid || !FlagOn(OldPte.u.Long, 0x800));
|
|
if (WasDirty != NULL)
|
|
{
|
|
*WasDirty = !!OldPte.u.Hard.Dirty;
|
|
}
|
|
if (Page != NULL)
|
|
{
|
|
*Page = OldPte.u.Hard.PageFrameNumber;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Process != NULL)
|
|
{
|
|
/* Remove PDE reference, if needed */
|
|
if (OldPte.u.Long != 0)
|
|
{
|
|
if (MiDecrementPageTableReferences(Address) == 0)
|
|
{
|
|
KIRQL OldIrql = MiAcquirePfnLock();
|
|
MiDeletePde(MiAddressToPde(Address), Process);
|
|
MiReleasePfnLock(OldIrql);
|
|
}
|
|
}
|
|
|
|
if (!IsPhysical && OldPte.u.Hard.Valid)
|
|
{
|
|
PMMPFN Pfn1;
|
|
KIRQL OldIrql;
|
|
|
|
OldIrql = MiAcquirePfnLock();
|
|
Pfn1 = &MmPfnDatabase[OldPte.u.Hard.PageFrameNumber];
|
|
ASSERT(Pfn1->u3.e1.PageLocation == ActiveAndValid);
|
|
ASSERT(Pfn1->u2.ShareCount > 0);
|
|
if (--Pfn1->u2.ShareCount == 0)
|
|
{
|
|
Pfn1->u3.e1.PageLocation = TransitionPage;
|
|
}
|
|
MiReleasePfnLock(OldIrql);
|
|
}
|
|
|
|
MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
}
|
|
|
|
return OldPte.u.Long != 0;
|
|
}
|
|
|
|
_Success_(return)
|
|
BOOLEAN
|
|
MmDeleteVirtualMapping(
|
|
_Inout_opt_ PEPROCESS Process,
|
|
_In_ PVOID Address,
|
|
_Out_opt_ BOOLEAN * WasDirty,
|
|
_Out_opt_ PPFN_NUMBER Page)
|
|
{
|
|
return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, FALSE);
|
|
}
|
|
|
|
_Success_(return)
|
|
BOOLEAN
|
|
MmDeletePhysicalMapping(
|
|
_Inout_opt_ PEPROCESS Process,
|
|
_In_ PVOID Address,
|
|
_Out_opt_ BOOLEAN * WasDirty,
|
|
_Out_opt_ PPFN_NUMBER Page)
|
|
{
|
|
return MmDeleteVirtualMappingEx(Process, Address, WasDirty, Page, TRUE);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MmDeletePageFileMapping(
|
|
PEPROCESS Process,
|
|
PVOID Address,
|
|
SWAPENTRY* SwapEntry)
|
|
{
|
|
PMMPTE PointerPte;
|
|
MMPTE OldPte;
|
|
|
|
/* This should not be called for kernel space anymore */
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Address < MmSystemRangeStart);
|
|
|
|
/* And we don't support deleting for other process */
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
/* And we should be at low IRQL */
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
/* We are tinkering with the PDE here. Ensure it will be there */
|
|
MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
/* Callers must ensure there is actually something there */
|
|
ASSERT(MiAddressToPde(Address)->u.Long != 0);
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
OldPte.u.Long = InterlockedExchangePte(PointerPte, 0);
|
|
/* This must be a swap entry ! */
|
|
if (!FlagOn(OldPte.u.Long, 0x800) || OldPte.u.Hard.Valid)
|
|
{
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, OldPte.u.Long, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
|
|
}
|
|
|
|
/* This used to be a non-zero PTE, now we can let the PDE go. */
|
|
if (MiDecrementPageTableReferences(Address) == 0)
|
|
{
|
|
/* We can let it go */
|
|
KIRQL OldIrql = MiAcquirePfnLock();
|
|
MiDeletePde(MiPteToPde(PointerPte), Process);
|
|
MiReleasePfnLock(OldIrql);
|
|
}
|
|
|
|
MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
*SwapEntry = OldPte.u.Long >> 1;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
MmIsPagePresent(PEPROCESS Process, PVOID Address)
|
|
{
|
|
BOOLEAN Ret;
|
|
|
|
if (Address >= MmSystemRangeStart)
|
|
{
|
|
ASSERT(Process == NULL);
|
|
#if _MI_PAGING_LEVELS == 2
|
|
if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
|
|
#else
|
|
if (!MiIsPdeForAddressValid(Address))
|
|
#endif
|
|
{
|
|
/* It can't be present if there is no PDE */
|
|
return FALSE;
|
|
}
|
|
|
|
return MiAddressToPte(Address)->u.Hard.Valid;
|
|
}
|
|
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
if (!MiIsPageTablePresent(Address))
|
|
{
|
|
/* It can't be present if there is no PDE */
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
return FALSE;
|
|
}
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
Ret = MiAddressToPte(Address)->u.Hard.Valid;
|
|
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
return Ret;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
MmIsDisabledPage(PEPROCESS Process, PVOID Address)
|
|
{
|
|
BOOLEAN Ret;
|
|
PMMPTE PointerPte;
|
|
|
|
if (Address >= MmSystemRangeStart)
|
|
{
|
|
ASSERT(Process == NULL);
|
|
#if _MI_PAGING_LEVELS == 2
|
|
if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
|
|
#else
|
|
if (!MiIsPdeForAddressValid(Address))
|
|
#endif
|
|
{
|
|
/* It's not disabled if it's not present */
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
if (!MiIsPageTablePresent(Address))
|
|
{
|
|
/* It can't be disabled if there is no PDE */
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
return FALSE;
|
|
}
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
}
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
Ret = !PointerPte->u.Hard.Valid
|
|
&& !FlagOn(PointerPte->u.Long, 0x800)
|
|
&& (PointerPte->u.Hard.PageFrameNumber != 0);
|
|
|
|
if (Address < MmSystemRangeStart)
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
return Ret;
|
|
}
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
MmIsPageSwapEntry(PEPROCESS Process, PVOID Address)
|
|
{
|
|
BOOLEAN Ret;
|
|
PMMPTE PointerPte;
|
|
|
|
/* We never set swap entries for kernel addresses */
|
|
if (Address >= MmSystemRangeStart)
|
|
{
|
|
ASSERT(Process == NULL);
|
|
return FALSE;
|
|
}
|
|
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
if (!MiIsPageTablePresent(Address))
|
|
{
|
|
/* There can't be a swap entry if there is no PDE */
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
return FALSE;
|
|
}
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
Ret = !PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800);
|
|
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
return Ret;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MmGetPageFileMapping(PEPROCESS Process, PVOID Address, SWAPENTRY* SwapEntry)
|
|
{
|
|
PMMPTE PointerPte;
|
|
|
|
/* We never set swap entries for kernel addresses */
|
|
if (Address >= MmSystemRangeStart)
|
|
{
|
|
ASSERT(Process == NULL);
|
|
*SwapEntry = 0;
|
|
return;
|
|
}
|
|
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
if (!MiIsPageTablePresent(Address))
|
|
{
|
|
/* There can't be a swap entry if there is no PDE */
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
*SwapEntry = 0;
|
|
return;
|
|
}
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
if (!PointerPte->u.Hard.Valid && FlagOn(PointerPte->u.Long, 0x800))
|
|
*SwapEntry = PointerPte->u.Long >> 1;
|
|
else
|
|
*SwapEntry = 0;
|
|
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreatePageFileMapping(PEPROCESS Process,
|
|
PVOID Address,
|
|
SWAPENTRY SwapEntry)
|
|
{
|
|
PMMPTE PointerPte;
|
|
ULONG_PTR Pte;
|
|
|
|
/* This should not be called for kernel space anymore */
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Address < MmSystemRangeStart);
|
|
|
|
/* And we don't support creating for other process */
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
if (SwapEntry & ((ULONG_PTR)1 << (RTL_BITS_OF(SWAPENTRY) - 1)))
|
|
{
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
/* We are tinkering with the PDE here. Ensure it will be there */
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
Pte = InterlockedExchangePte(PointerPte, SwapEntry << 1);
|
|
if (Pte != 0)
|
|
{
|
|
KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
|
|
}
|
|
|
|
/* This used to be a 0 PTE, now we need a valid PDE to keep it around */
|
|
MiIncrementPageTableReferences(Address);
|
|
MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreateVirtualMappingUnsafeEx(
|
|
_Inout_opt_ PEPROCESS Process,
|
|
_In_ PVOID Address,
|
|
_In_ ULONG flProtect,
|
|
_In_ PFN_NUMBER Page,
|
|
_In_ BOOLEAN IsPhysical)
|
|
{
|
|
ULONG ProtectionMask;
|
|
PMMPTE PointerPte;
|
|
MMPTE TempPte;
|
|
ULONG_PTR Pte;
|
|
|
|
DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %x)\n",
|
|
Process, Address, flProtect, Page);
|
|
|
|
ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
|
|
|
|
ProtectionMask = MiMakeProtectionMask(flProtect);
|
|
/* Caller must have checked ! */
|
|
ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
|
|
ASSERT(ProtectionMask != MM_NOACCESS);
|
|
ASSERT(ProtectionMask != MM_ZERO_ACCESS);
|
|
|
|
/* Make sure our PDE is valid, and that everything is going fine */
|
|
if (Process == NULL)
|
|
{
|
|
/* We don't support this in legacy Mm for kernel mappings */
|
|
ASSERT(ProtectionMask != MM_WRITECOPY);
|
|
ASSERT(ProtectionMask != MM_EXECUTE_WRITECOPY);
|
|
|
|
if (Address < MmSystemRangeStart)
|
|
{
|
|
DPRINT1("NULL process given for user-mode mapping at %p\n", Address);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
#if _MI_PAGING_LEVELS == 2
|
|
if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
|
|
MiFillSystemPageDirectory(Address, PAGE_SIZE);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if ((Address >= MmSystemRangeStart) || Add2Ptr(Address, PAGE_SIZE) >= MmSystemRangeStart)
|
|
{
|
|
DPRINT1("Process %p given for kernel-mode mapping at %p -- 1 page starting at %Ix\n",
|
|
Process, Address, Page);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
/* Only for current process !!! */
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
}
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
|
|
MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, ProtectionMask, Page);
|
|
|
|
Pte = InterlockedExchangePte(PointerPte, TempPte.u.Long);
|
|
/* There should not have been anything valid here */
|
|
if (Pte != 0)
|
|
{
|
|
DPRINT1("Bad PTE %lx at %p for %p\n", Pte, PointerPte, Address);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
if (!IsPhysical)
|
|
{
|
|
PMMPFN Pfn1;
|
|
KIRQL OldIrql;
|
|
|
|
OldIrql = MiAcquirePfnLock();
|
|
Pfn1 = &MmPfnDatabase[TempPte.u.Hard.PageFrameNumber];
|
|
Pfn1->u2.ShareCount++;
|
|
Pfn1->u3.e1.PageLocation = ActiveAndValid;
|
|
MiReleasePfnLock(OldIrql);
|
|
}
|
|
|
|
/* We don't need to flush the TLB here because it only caches valid translations
|
|
* and we're moving this PTE from invalid to valid so it can't be cached right now */
|
|
|
|
if (Address < MmSystemRangeStart)
|
|
{
|
|
/* Add PDE reference */
|
|
MiIncrementPageTableReferences(Address);
|
|
MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreateVirtualMappingUnsafe(
|
|
_Inout_opt_ PEPROCESS Process,
|
|
_In_ PVOID Address,
|
|
_In_ ULONG flProtect,
|
|
_In_ PFN_NUMBER Page)
|
|
{
|
|
return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, FALSE);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreatePhysicalMapping(
|
|
_Inout_opt_ PEPROCESS Process,
|
|
_In_ PVOID Address,
|
|
_In_ ULONG flProtect,
|
|
_In_ PFN_NUMBER Page)
|
|
{
|
|
return MmCreateVirtualMappingUnsafeEx(Process, Address, flProtect, Page, TRUE);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmCreateVirtualMapping(PEPROCESS Process,
|
|
PVOID Address,
|
|
ULONG flProtect,
|
|
PFN_NUMBER Page)
|
|
{
|
|
ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
|
|
if (!MmIsPageInUse(Page))
|
|
{
|
|
DPRINT1("Page %lx is not in use\n", Page);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
return MmCreateVirtualMappingUnsafe(Process, Address, flProtect, Page);
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
MmGetPageProtect(PEPROCESS Process, PVOID Address)
|
|
{
|
|
PMMPTE PointerPte;
|
|
ULONG Protect;
|
|
|
|
if (Address >= MmSystemRangeStart)
|
|
{
|
|
ASSERT(Process == NULL);
|
|
|
|
#if _MI_PAGING_LEVELS == 2
|
|
if (!MiSynchronizeSystemPde(MiAddressToPde(Address)))
|
|
#else
|
|
if (!MiIsPdeForAddressValid(Address))
|
|
#endif
|
|
{
|
|
return PAGE_NOACCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(Address < MmSystemRangeStart);
|
|
ASSERT(Process != NULL);
|
|
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
MiLockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
if (!MiIsPageTablePresent(Address))
|
|
{
|
|
/* It can't be present if there is no PDE */
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
return PAGE_NOACCESS;
|
|
}
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
}
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
|
|
if (!PointerPte->u.Flush.Valid)
|
|
{
|
|
Protect = PAGE_NOACCESS;
|
|
}
|
|
else
|
|
{
|
|
if (PointerPte->u.Flush.CopyOnWrite)
|
|
Protect = PAGE_WRITECOPY;
|
|
else if (PointerPte->u.Flush.Write)
|
|
Protect = PAGE_READWRITE;
|
|
else
|
|
Protect = PAGE_READONLY;
|
|
#if _MI_PAGING_LEVELS >= 3
|
|
/* PAE & AMD64 long mode support NoExecute bit */
|
|
if (!PointerPte->u.Flush.NoExecute)
|
|
Protect <<= 4;
|
|
#endif
|
|
if (PointerPte->u.Flush.CacheDisable)
|
|
Protect |= PAGE_NOCACHE;
|
|
if (PointerPte->u.Flush.WriteThrough)
|
|
Protect |= PAGE_WRITETHROUGH;
|
|
}
|
|
|
|
if (Address < MmSystemRangeStart)
|
|
MiUnlockProcessWorkingSetShared(Process, PsGetCurrentThread());
|
|
|
|
return(Protect);
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MmSetPageProtect(PEPROCESS Process, PVOID Address, ULONG flProtect)
|
|
{
|
|
ULONG ProtectionMask;
|
|
PMMPTE PointerPte;
|
|
MMPTE TempPte, OldPte;
|
|
|
|
DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
|
|
Process, Address, flProtect);
|
|
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Address < MmSystemRangeStart);
|
|
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
ProtectionMask = MiMakeProtectionMask(flProtect);
|
|
/* Caller must have checked ! */
|
|
ASSERT(ProtectionMask != MM_INVALID_PROTECTION);
|
|
|
|
MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
|
|
/* Sanity check */
|
|
ASSERT(PointerPte->u.Hard.Owner == 1);
|
|
|
|
TempPte.u.Long = 0;
|
|
TempPte.u.Hard.PageFrameNumber = PointerPte->u.Hard.PageFrameNumber;
|
|
TempPte.u.Long |= MmProtectToPteMask[ProtectionMask];
|
|
TempPte.u.Hard.Owner = 1;
|
|
|
|
/* Only set valid bit if we have to */
|
|
if ((ProtectionMask != MM_NOACCESS) && !FlagOn(ProtectionMask, MM_GUARDPAGE))
|
|
TempPte.u.Hard.Valid = 1;
|
|
|
|
/* Keep dirty & accessed bits */
|
|
TempPte.u.Hard.Accessed = PointerPte->u.Hard.Accessed;
|
|
TempPte.u.Hard.Dirty = PointerPte->u.Hard.Dirty;
|
|
|
|
OldPte.u.Long = InterlockedExchangePte(PointerPte, TempPte.u.Long);
|
|
|
|
// We should be able to bring a page back from PAGE_NOACCESS
|
|
if (!OldPte.u.Hard.Valid && (FlagOn(OldPte.u.Long, 0x800) || (OldPte.u.Hard.PageFrameNumber == 0)))
|
|
{
|
|
DPRINT1("Invalid Pte %lx\n", OldPte.u.Long);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
if (OldPte.u.Long != TempPte.u.Long)
|
|
KeInvalidateTlbEntry(Address);
|
|
|
|
MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
MmSetDirtyBit(PEPROCESS Process, PVOID Address, BOOLEAN Bit)
|
|
{
|
|
PMMPTE PointerPte;
|
|
|
|
DPRINT("MmSetDirtyBit(Process %p Address %p Bit %x)\n",
|
|
Process, Address, Bit);
|
|
|
|
ASSERT(Process != NULL);
|
|
ASSERT(Address < MmSystemRangeStart);
|
|
|
|
ASSERT(Process == PsGetCurrentProcess());
|
|
|
|
MiLockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
|
|
MiMakePdeExistAndMakeValid(MiAddressToPde(Address), Process, MM_NOIRQL);
|
|
|
|
PointerPte = MiAddressToPte(Address);
|
|
// We shouldnl't set dirty bit on non-mapped addresses
|
|
if (!PointerPte->u.Hard.Valid && (FlagOn(PointerPte->u.Long, 0x800) || (PointerPte->u.Hard.PageFrameNumber == 0)))
|
|
{
|
|
DPRINT1("Invalid Pte %lx\n", PointerPte->u.Long);
|
|
KeBugCheck(MEMORY_MANAGEMENT);
|
|
}
|
|
|
|
PointerPte->u.Hard.Dirty = !!Bit;
|
|
|
|
if (!Bit)
|
|
KeInvalidateTlbEntry(Address);
|
|
|
|
MiUnlockProcessWorkingSetUnsafe(Process, PsGetCurrentThread());
|
|
}
|
|
|
|
CODE_SEG("INIT")
|
|
VOID
|
|
NTAPI
|
|
MmInitGlobalKernelPageDirectory(VOID)
|
|
{
|
|
/* Nothing to do here */
|
|
}
|
|
|
|
#ifdef _M_IX86
|
|
BOOLEAN
|
|
Mmi386MakeKernelPageTableGlobal(PVOID Address)
|
|
{
|
|
PMMPDE PointerPde = MiAddressToPde(Address);
|
|
PMMPTE PointerPte = MiAddressToPte(Address);
|
|
|
|
if (PointerPde->u.Hard.Valid == 0)
|
|
{
|
|
if (!MiSynchronizeSystemPde(PointerPde))
|
|
return FALSE;
|
|
return PointerPte->u.Hard.Valid != 0;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
/* EOF */
|