#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
-#include "../ARM3/miarm.h"
+#include <mm/ARM3/miarm.h>
#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, MmInitGlobalKernelPageDirectory)
-#pragma alloc_text(INIT, MiInitPageDirectoryMap)
#endif
+#define ADDR_TO_PDE_OFFSET MiAddressToPdeOffset
+#define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (1024 * PAGE_SIZE))
/* GLOBALS *****************************************************************/
#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 IS_HYPERSPACE(v) (((ULONG)(v) >= HYPER_SPACE && (ULONG)(v) <= HYPER_SPACE_END))
#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
+#define PAGE_MASK(x) ((x)&(~0xfff))
const
ULONG
/* FUNCTIONS ***************************************************************/
-BOOLEAN MmUnmapPageTable(PULONG Pt);
+static BOOLEAN MmUnmapPageTable(PULONG Pt);
VOID
MiFlushTlb(PULONG Pt, PVOID Address)
return(Attributes);
}
-static
-VOID
-MmDeletePageDirectoryEntry(ULONG PdeEntry)
-{
- KIRQL OldIrql;
- PMMPFN Page;
-
- Page = MiGetPfnEntry(PTE_TO_PFN(PdeEntry));
-
- /* Check if this is a legacy allocation */
- if (MI_IS_ROS_PFN(Page))
- {
- /* Free it using the legacy API */
- MmReleasePageMemoryConsumer(MC_SYSTEM, PTE_TO_PFN(PdeEntry));
- }
- else
- {
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-
- /* Free it using the ARM3 API */
- MI_SET_PFN_DELETED(Page);
- MiDecrementShareCount(Page, PTE_TO_PFN(PdeEntry));
-
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
- }
-}
-
-VOID
+NTSTATUS
NTAPI
-MmDeleteProcessPageDirectory(PEPROCESS Process)
-{
- PULONG PageDir;
- ULONG PdeOffset;
+MiDispatchFault(IN BOOLEAN StoreInstruction,
+ IN PVOID Address,
+ IN PMMPTE PointerPte,
+ IN PMMPTE PointerProtoPte,
+ IN BOOLEAN Recursive,
+ IN PEPROCESS Process,
+ IN PVOID TrapInformation,
+ IN PVOID Vad);
- /* Map the page directory in hyperspace */
- PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
-
- /* Loop the user land page directory */
- for (PdeOffset = 0; PdeOffset < ADDR_TO_PDE_OFFSET(MmSystemRangeStart); PdeOffset++)
- {
- /* Check if a valid PDE exists here */
- if (PageDir[PdeOffset] != 0)
- {
- /* Free the page that backs it */
- MmDeletePageDirectoryEntry(PageDir[PdeOffset]);
- }
- }
-
- /* Free the hyperspace mapping page (ARM3) */
- MmDeletePageDirectoryEntry(PageDir[ADDR_TO_PDE_OFFSET(HYPERSPACE)]);
-
- /* Delete the hyperspace mapping */
- MmDeleteHyperspaceMapping(PageDir);
-
- /* Free the PDE page itself (ARM3) */
- MmDeletePageDirectoryEntry(Process->Pcb.DirectoryTableBase[0]);
-}
+NTSTATUS
+NTAPI
+MiFillSystemPageDirectory(IN PVOID Base,
+ IN SIZE_T NumberOfBytes);
static PULONG
MmGetPageTableForProcess(PEPROCESS Process, PVOID Address, BOOLEAN Create)
{
- ULONG PdeOffset = ADDR_TO_PDE_OFFSET(Address);
- NTSTATUS Status;
PFN_NUMBER Pfn;
- ULONG Entry;
- PULONG Pt, PageDir;
+ PULONG Pt;
+ PMMPDE PointerPde;
if (Address < MmSystemRangeStart)
{
if(Process != PsGetCurrentProcess())
{
- PageDir = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
- if (PageDir == NULL)
+ PMMPDE PdeBase;
+ ULONG PdeOffset = MiGetPdeOffset(Address);
+
+ /* Nobody but page fault should ask for creating the PDE,
+ * Which imples that Process is the current one */
+ ASSERT(Create == FALSE);
+
+ PdeBase = MmCreateHyperspaceMapping(PTE_TO_PFN(Process->Pcb.DirectoryTableBase[0]));
+ if (PdeBase == NULL)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
- if (0 == InterlockedCompareExchangePte(&PageDir[PdeOffset], 0, 0))
+ PointerPde = PdeBase + PdeOffset;
+ if (PointerPde->u.Hard.Valid == 0)
{
- if (Create == FALSE)
- {
- MmDeleteHyperspaceMapping(PageDir);
- return NULL;
- }
- MI_SET_USAGE(MI_USAGE_LEGACY_PAGE_DIRECTORY);
-
- MI_SET_PROCESS2(Process->ImageFileName);
-
- Status = MmRequestPageMemoryConsumer(MC_SYSTEM, 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_SYSTEM, Pfn);
- Pfn = PTE_TO_PFN(Entry);
- }
+ MmDeleteHyperspaceMapping(PdeBase);
+ return NULL;
}
else
{
- Pfn = PTE_TO_PFN(PageDir[PdeOffset]);
+ Pfn = PointerPde->u.Hard.PageFrameNumber;
}
- MmDeleteHyperspaceMapping(PageDir);
+ MmDeleteHyperspaceMapping(PdeBase);
Pt = MmCreateHyperspaceMapping(Pfn);
if (Pt == NULL)
{
return Pt + MiAddressToPteOffset(Address);
}
/* This is for our process */
- PageDir = (PULONG)MiAddressToPde(Address);
- if (0 == InterlockedCompareExchangePte(PageDir, 0, 0))
+ PointerPde = MiAddressToPde(Address);
+ Pt = (PULONG)MiAddressToPte(Address);
+ if (PointerPde->u.Hard.Valid == 0)
{
+ NTSTATUS Status;
if (Create == FALSE)
{
return NULL;
}
- MI_SET_USAGE(MI_USAGE_LEGACY_PAGE_DIRECTORY);
- MI_SET_PROCESS2(Process->ImageFileName);
-
- Status = MmRequestPageMemoryConsumer(MC_SYSTEM, 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_SYSTEM, Pfn);
- }
+ ASSERT(PointerPde->u.Long == 0);
+
+ MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
+ Status = MiDispatchFault(TRUE,
+ Pt,
+ PointerPde,
+ NULL,
+ FALSE,
+ PsGetCurrentProcess(),
+ NULL,
+ NULL);
+ DBG_UNREFERENCED_LOCAL_VARIABLE(Status);
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ ASSERT(PointerPde->u.Hard.Valid == 1);
}
return (PULONG)MiAddressToPte(Address);
}
/* This is for kernel land address */
- PageDir = (PULONG)MiAddressToPde(Address);
- if (0 == InterlockedCompareExchangePte(PageDir, 0, 0))
+ ASSERT(Process == NULL);
+ PointerPde = MiAddressToPde(Address);
+ Pt = (PULONG)MiAddressToPte(Address);
+ if (PointerPde->u.Hard.Valid == 0)
{
- if (0 == InterlockedCompareExchangePte(&MmGlobalKernelPageDirectory[PdeOffset], 0, 0))
+ /* Let ARM3 synchronize the PDE */
+ if(!MiSynchronizeSystemPde(PointerPde))
{
- if (Create == FALSE)
- {
+ /* PDE (still) not valid, let ARM3 allocate one if asked */
+ if(Create == FALSE)
return NULL;
- }
- MI_SET_USAGE(MI_USAGE_LEGACY_PAGE_DIRECTORY);
- if (Process) MI_SET_PROCESS2(Process->ImageFileName);
- if (!Process) MI_SET_PROCESS2("Kernel Legacy");
- 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]);
- return (PULONG)MiAddressToPte(Address);
+ MiFillSystemPageDirectory(Address, PAGE_SIZE);
}
- InterlockedExchangePte(PageDir, MmGlobalKernelPageDirectory[PdeOffset]);
}
- return (PULONG)MiAddressToPte(Address);
+ return Pt;
}
-BOOLEAN MmUnmapPageTable(PULONG Pt)
+static BOOLEAN MmUnmapPageTable(PULONG Pt)
{
if (!IS_HYPERSPACE(Pt))
{
VOID
NTAPI
-MmDisableVirtualMapping(PEPROCESS Process, PVOID Address, BOOLEAN* WasDirty, PPFN_NUMBER 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 = (Pte & PA_PRESENT);
- 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,
+MmDeleteVirtualMapping(PEPROCESS Process, PVOID Address,
BOOLEAN* WasDirty, PPFN_NUMBER Page)
/*
* FUNCTION: Delete a virtual mapping
ULONG Pte;
PULONG Pt;
- DPRINT("MmDeleteVirtualMapping(%x, %x, %d, %x, %x)\n",
- Process, Address, FreePage, WasDirty, Page);
+ DPRINT("MmDeleteVirtualMapping(%p, %p, %p, %p)\n",
+ Process, Address, WasDirty, Page);
Pt = MmGetPageTableForProcess(Process, Address, FALSE);
*/
Pte = InterlockedExchangePte(Pt, 0);
- WasValid = (Pte & PA_PRESENT);
+ /* We count a mapping as valid if it's a present page, or it's a nonzero pfn with
+ * the swap bit unset, indicating a valid page protected to PAGE_NOACCESS. */
+ WasValid = (Pte & PA_PRESENT) || ((Pte >> PAGE_SHIFT) && !(Pte & 0x800));
if (WasValid)
{
/* Flush the TLB since we transitioned this PTE
* are removed from the cache */
MiFlushTlb(Pt, Address);
- Pfn = PTE_TO_PFN(Pte);
+ if (Address < MmSystemRangeStart)
+ {
+ /* Remove PDE reference */
+ Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
+ ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
+ }
- if (FreePage)
- {
- MmReleasePageMemoryConsumer(MC_SYSTEM, Pfn);
- Pfn = 0;
- }
+ Pfn = PTE_TO_PFN(Pte);
}
else
{
*/
Pte = InterlockedExchangePte(Pt, 0);
+ if (Address < MmSystemRangeStart)
+ {
+ /* Remove PDE reference */
+ Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]--;
+ ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] < PTE_COUNT);
+ }
+
/* We don't need to flush here because page file entries
* are invalid translations, so the processor won't cache them */
MmUnmapPageTable(Pt);
if ((Pte & PA_PRESENT) || !(Pte & 0x800))
{
+ DPRINT1("Pte %x (want not 1 and 0x800)\n", Pte);
KeBugCheck(MEMORY_MANAGEMENT);
}
}
BOOLEAN
-Mmi386MakeKernelPageTableGlobal(PVOID PAddress)
+Mmi386MakeKernelPageTableGlobal(PVOID Address)
{
- PULONG Pt, Pde;
- Pde = (PULONG)MiAddressToPde(PAddress);
- if (*Pde == 0)
+ PMMPDE PointerPde = MiAddressToPde(Address);
+ PMMPTE PointerPte = MiAddressToPte(Address);
+
+ if (PointerPde->u.Hard.Valid == 0)
{
- Pt = MmGetPageTableForProcess(NULL, PAddress, FALSE);
- if (Pt != NULL)
- {
- return TRUE;
- }
+ if(!MiSynchronizeSystemPde(PointerPde))
+ return FALSE;
+ return PointerPte->u.Hard.Valid != 0;
}
- return(FALSE);
+ return FALSE;
}
BOOLEAN
}
}
-VOID
+BOOLEAN
NTAPI
-MmEnableVirtualMapping(PEPROCESS Process, PVOID Address)
+MmIsPagePresent(PEPROCESS Process, PVOID Address)
{
- PULONG Pt;
- ULONG Pte;
-
- Pt = MmGetPageTableForProcess(Process, Address, FALSE);
- if (Pt == NULL)
- {
- //HACK to get DPH working, waiting for MM rewrite :-/
- //KeBugCheck(MEMORY_MANAGEMENT);
- return;
- }
-
- /* Do not mark a 0 page as present */
- if(0 == InterlockedCompareExchangePte(Pt, 0, 0))
- return;
-
- do
- {
- Pte = *Pt;
- } while (Pte != InterlockedCompareExchangePte(Pt, Pte | PA_PRESENT, Pte));
-
- /* We don't need to flush the TLB here because it
- * won't cache translations for non-present pages */
- MmUnmapPageTable(Pt);
+ return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
}
BOOLEAN
NTAPI
-MmIsPagePresent(PEPROCESS Process, PVOID Address)
+MmIsDisabledPage(PEPROCESS Process, PVOID Address)
{
- return MmGetPageEntryForProcess(Process, Address) & PA_PRESENT;
+ ULONG_PTR Entry = MmGetPageEntryForProcess(Process, Address);
+ return !(Entry & PA_PRESENT) && !(Entry & 0x800) && (Entry >> PAGE_SHIFT);
}
BOOLEAN
KeBugCheck(MEMORY_MANAGEMENT);
}
- Pt = MmGetPageTableForProcess(Process, Address, TRUE);
+ Pt = MmGetPageTableForProcess(Process, Address, FALSE);
if (Pt == NULL)
{
- KeBugCheck(MEMORY_MANAGEMENT);
+ /* Nobody should page out an address that hasn't even been mapped */
+ /* But we might place a wait entry first, requiring the page table */
+ if (SwapEntry != MM_WAIT_ENTRY)
+ {
+ KeBugCheck(MEMORY_MANAGEMENT);
+ }
+ Pt = MmGetPageTableForProcess(Process, Address, TRUE);
}
Pte = InterlockedExchangePte(Pt, SwapEntry << 1);
if (Pte != 0)
{
- KeBugCheck(MEMORY_MANAGEMENT);
+ KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
}
+ if (Address < MmSystemRangeStart)
+ {
+ /* Add PDE reference */
+ Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
+ ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
+ }
+
/* We don't need to flush the TLB here because it
* only caches valid translations and a zero PTE
* is not a valid translation */
ULONG oldPdeOffset, PdeOffset;
PULONG Pt = NULL;
ULONG Pte;
- DPRINT("MmCreateVirtualMappingUnsafe(%x, %x, %x, %x (%x), %d)\n",
+ DPRINT("MmCreateVirtualMappingUnsafe(%p, %p, %lu, %p (%x), %lu)\n",
Process, Address, flProtect, Pages, *Pages, PageCount);
ASSERT(((ULONG_PTR)Address % PAGE_SIZE) == 0);
if (!(Attributes & PA_PRESENT) && Pages[i] != 0)
{
DPRINT1("Setting physical address but not allowing access at address "
- "0x%.8X with attributes %x/%x.\n",
+ "0x%p with attributes %x/%x.\n",
Addr, Attributes, flProtect);
KeBugCheck(MEMORY_MANAGEMENT);
}
/* There should not be anything valid here */
if (Pte != 0)
{
- DPRINT1("Bad PTE %lx\n", Pte);
+ DPRINT1("Bad PTE %lx at %p for %p + %lu\n", Pte, Pt, Address, i);
KeBugCheck(MEMORY_MANAGEMENT);
}
/* 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 (Addr < MmSystemRangeStart)
+ {
+ /* Add PDE reference */
+ Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)]++;
+ ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Addr)] <= PTE_COUNT);
+ }
}
ASSERT(Addr > Address);
{
ULONG i;
+ ASSERT((ULONG_PTR)Address % PAGE_SIZE == 0);
for (i = 0; i < PageCount; i++)
{
if (!MmIsPageInUse(Pages[i]))
PULONG Pt;
ULONG Pte;
- DPRINT("MmSetPageProtect(Process %x Address %x flProtect %x)\n",
+ DPRINT("MmSetPageProtect(Process %p Address %p flProtect %x)\n",
Process, Address, flProtect);
Attributes = ProtectToPTE(flProtect);
}
Pte = InterlockedExchangePte(Pt, PAGE_MASK(*Pt) | Attributes | (*Pt & (PA_ACCESSED|PA_DIRTY)));
- if (!(Pte & PA_PRESENT))
+ // We should be able to bring a page back from PAGE_NOACCESS
+ if ((Pte & 0x800) || !(Pte >> PAGE_SHIFT))
{
DPRINT1("Invalid Pte %lx\n", Pte);
KeBugCheck(MEMORY_MANAGEMENT);
MmUnmapPageTable(Pt);
}
-/*
- * @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
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];
- }
- }
+ /* Nothing to do here */
}
/* EOF */