Author: sir_richard
Date: Mon Oct 4 18:51:07 2010
New Revision: 48979
URL: http://svn.reactos.org/svn/reactos?rev=48979&view=rev
Log:
[NTOS]: Implement/fixup the code paths during page faults that are needed to succesfuly resolve a demand page associated with a pagefile backed ARM3 section (which uses Prototype PTEs). A lot of the code was already there but assumed we were using Prototype PTEs only for the shared user data page. By combining that code with the typical demand-zero fault code, we obtain the needed paths. For now, only tested with ARM3 sections that are page-filed backed (not image or data-file backed) mapped into system view space (MmMapViewOfSectionInSystemSpace), not user-mode addresses (which need VADs). The code to actually create/map these doesn't exist in trunk yet, the purpose of this checkin is to test the new fault changes to make sure they don't cause negative effects to already-working faults.
Modified:
trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c
Modified: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/pagfault.…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] Mon Oct 4 18:51:07 2010
@@ -176,15 +176,14 @@
{
PFN_NUMBER PageFrameNumber = 0;
MMPTE TempPte;
- BOOLEAN NeedZero = FALSE;
+ BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
ULONG Color;
DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
Address,
Process);
/* Must currently only be called by paging path */
- ASSERT(OldIrql == MM_NOIRQL);
- if (Process)
+ if ((Process) && (OldIrql == MM_NOIRQL))
{
/* Sanity check */
ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
@@ -194,45 +193,64 @@
/* Get process color */
Color = MI_GET_NEXT_PROCESS_COLOR(Process);
+ ASSERT(Color != 0xFFFFFFFF);
/* We'll need a zero page */
NeedZero = TRUE;
}
else
{
+ /* Check if we need a zero page */
+ NeedZero = (OldIrql != MM_NOIRQL);
+
/* Get the next system page color */
Color = MI_GET_NEXT_COLOR();
}
-
- //
- // Lock the PFN database
- //
- OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ /* Check if the PFN database should be acquired */
+ if (OldIrql == MM_NOIRQL)
+ {
+ /* Acquire it and remember we should release it after */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ HaveLock = TRUE;
+ }
+
+ /* We either manually locked the PFN DB, or already came with it locked */
+ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ /* Do we need a zero page? */
ASSERT(PointerPte->u.Hard.Valid == 0);
-
- /* Do we need a zero page? */
- if (NeedZero)
+ if ((NeedZero) && (Process))
{
/* Try to get one, if we couldn't grab a free page and zero it */
PageFrameNumber = MiRemoveZeroPageSafe(Color);
- if (PageFrameNumber) NeedZero = FALSE;
- }
-
- /* Did we get a page? */
- if (!PageFrameNumber)
- {
- /* We either failed to find a zero page, or this is a system request */
+ if (PageFrameNumber)
+ {
+ /* We got a genuine zero page, stop worrying about it */
+ NeedZero = FALSE;
+ }
+ else
+ {
+ /* We'll need a free page and zero it manually */
+ PageFrameNumber = MiRemoveAnyPage(Color);
+ }
+ }
+ else if (!NeedZero)
+ {
+ /* Process or system doesn't want a zero page, grab anything */
PageFrameNumber = MiRemoveAnyPage(Color);
- DPRINT("New pool page: %lx\n", PageFrameNumber);
+ }
+ else
+ {
+ /* System wants a zero page, obtain one */
+ PageFrameNumber = MiRemoveZeroPage(Color);
}
/* Initialize it */
MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
- //
- // Release PFN lock
- //
- KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ /* Release PFN lock if needed */
+ if (HaveLock) KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
//
// Increment demand zero faults
@@ -283,24 +301,53 @@
IN PMMPFN Pfn1)
{
MMPTE TempPte;
+ PMMPTE OriginalPte;
+ ULONG Protection;
PFN_NUMBER PageFrameIndex;
/* Must be called with an valid prototype PTE, with the PFN lock held */
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
ASSERT(PointerProtoPte->u.Hard.Valid == 1);
- /* Quick-n-dirty */
- ASSERT(PointerPte->u.Soft.PageFileHigh == 0xFFFFF);
-
/* Get the page */
PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
+
+ /* Get the PFN entry and set it as a prototype PTE */
+ Pfn1 = MiGetPfnEntry(PageFrameIndex);
+ Pfn1->u3.e1.PrototypePte = 1;
+
+ /* FIXME: Increment the share count for the page table */
+
+ /* Check where we should be getting the protection information from */
+ if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
+ {
+ /* Get the protection from the PTE, there's no real Proto PTE data */
+ Protection = PointerPte->u.Soft.Protection;
+ }
+ else
+ {
+ /* Get the protection from the original PTE link */
+ OriginalPte = &Pfn1->OriginalPte;
+ Protection = OriginalPte->u.Soft.Protection;
+ }
/* Release the PFN lock */
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
- /* Build the user PTE */
- ASSERT(Address < MmSystemRangeStart);
- MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, MM_READONLY, PageFrameIndex);
+ /* Remove caching bits */
+ Protection &= ~(MM_NOCACHE | MM_NOACCESS);
+
+ /* Check if this is a kernel or user address */
+ if (Address < MmSystemRangeStart)
+ {
+ /* Build the user PTE */
+ MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
+ }
+ else
+ {
+ /* Build the kernel PTE */
+ MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
+ }
/* Write the PTE */
MI_WRITE_VALID_PTE(PointerPte, TempPte);
@@ -325,25 +372,56 @@
MMPTE TempPte;
PMMPFN Pfn1;
PFN_NUMBER PageFrameIndex;
+ NTSTATUS Status;
/* Must be called with an invalid, prototype PTE, with the PFN lock held */
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
ASSERT(PointerPte->u.Hard.Valid == 0);
ASSERT(PointerPte->u.Soft.Prototype == 1);
- /* Read the prototype PTE -- it must be valid since we only handle shared data */
+ /* Read the prototype PTE and check if it's valid */
TempPte = *PointerProtoPte;
- ASSERT(TempPte.u.Hard.Valid == 1);
-
- /* One more user of this mapped page */
- PageFrameIndex = PFN_FROM_PTE(&TempPte);
- Pfn1 = MiGetPfnEntry(PageFrameIndex);
- Pfn1->u2.ShareCount++;
-
- /* Call it a transition */
- InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
+ if (TempPte.u.Hard.Valid == 1)
+ {
+ /* One more user of this mapped page */
+ PageFrameIndex = PFN_FROM_PTE(&TempPte);
+ Pfn1 = MiGetPfnEntry(PageFrameIndex);
+ Pfn1->u2.ShareCount++;
+
+ /* Call it a transition */
+ InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
+
+ /* Complete the prototype PTE fault -- this will release the PFN lock */
+ return MiCompleteProtoPteFault(StoreInstruction,
+ Address,
+ PointerPte,
+ PointerProtoPte,
+ OldIrql,
+ NULL);
+ }
+
+ /* Make sure there's some protection mask */
+ if (TempPte.u.Long == 0)
+ {
+ /* Release the lock */
+ DPRINT1("Access on reserved section?\n");
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* This is the only thing we support right now */
+ ASSERT(TempPte.u.Soft.PageFileHigh == 0);
+ ASSERT(TempPte.u.Proto.ReadOnly == 0);
+ ASSERT(PointerPte > MiHighestUserPte);
+ ASSERT(TempPte.u.Soft.Prototype == 0);
+ ASSERT(TempPte.u.Soft.Transition == 0);
+
+ /* Resolve the demand zero fault */
+ Status = MiResolveDemandZeroFault(Address, PointerProtoPte, Process, OldIrql);
+ ASSERT(NT_SUCCESS(Status));
/* Complete the prototype PTE fault -- this will release the PFN lock */
+ ASSERT(PointerPte->u.Hard.Valid == 0);
return MiCompleteProtoPteFault(StoreInstruction,
Address,
PointerPte,
@@ -388,39 +466,76 @@
{
/* This should never happen */
ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
+
+ /* Check if this is a kernel-mode address */
+ SuperProtoPte = MiAddressToPte(PointerProtoPte);
+ if (Address >= MmSystemRangeStart)
+ {
+ /* Lock the PFN database */
+ LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
- /* We currently only handle the shared user data PTE path */
- ASSERT(Address < MmSystemRangeStart);
- ASSERT(PointerPte->u.Soft.Prototype == 1);
- ASSERT(PointerPte->u.Soft.PageFileHigh == 0xFFFFF);
- ASSERT(Vad == NULL);
-
- /* Lock the PFN database */
- LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
-
- /* For the shared data page, this should be true */
- SuperProtoPte = MiAddressToPte(PointerProtoPte);
- ASSERT(SuperProtoPte->u.Hard.Valid == 1);
- ASSERT(TempPte.u.Hard.Valid == 0);
-
- /* Resolve the fault -- this will release the PFN lock */
- Status = MiResolveProtoPteFault(StoreInstruction,
- Address,
- PointerPte,
- PointerProtoPte,
- NULL,
- NULL,
- NULL,
- Process,
- LockIrql,
- TrapInformation);
- ASSERT(Status == STATUS_SUCCESS);
-
- /* Complete this as a transition fault */
- ASSERT(OldIrql == KeGetCurrentIrql());
- ASSERT(OldIrql <= APC_LEVEL);
- ASSERT(KeAreAllApcsDisabled() == TRUE);
- return STATUS_PAGE_FAULT_TRANSITION;
+ /* Has the PTE been made valid yet? */
+ if (!SuperProtoPte->u.Hard.Valid)
+ {
+ UNIMPLEMENTED;
+ while (TRUE);
+ }
+ else
+ {
+ ASSERT(PointerPte->u.Hard.Valid == 0);
+ /* Resolve the fault -- this will release the PFN lock */
+ Status = MiResolveProtoPteFault(StoreInstruction,
+ Address,
+ PointerPte,
+ PointerProtoPte,
+ NULL,
+ NULL,
+ NULL,
+ Process,
+ LockIrql,
+ TrapInformation);
+ ASSERT(Status == STATUS_SUCCESS);
+
+ /* Complete this as a transition fault */
+ ASSERT(OldIrql == KeGetCurrentIrql());
+ ASSERT(OldIrql <= APC_LEVEL);
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ return Status;
+ }
+ }
+ else
+ {
+ /* We currently only handle the shared user data PTE path */
+ ASSERT(PointerPte->u.Soft.Prototype == 1);
+ ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
+ ASSERT(Vad == NULL);
+
+ /* Lock the PFN database */
+ LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ /* For the shared data page, this should be true */
+ ASSERT(SuperProtoPte->u.Hard.Valid == 1);
+ ASSERT(TempPte.u.Hard.Valid == 0);
+
+ /* Resolve the fault -- this will release the PFN lock */
+ Status = MiResolveProtoPteFault(StoreInstruction,
+ Address,
+ PointerPte,
+ PointerProtoPte,
+ NULL,
+ NULL,
+ NULL,
+ Process,
+ LockIrql,
+ TrapInformation);
+ ASSERT(Status == STATUS_SUCCESS);
+
+ /* Complete this as a transition fault */
+ ASSERT(OldIrql == KeGetCurrentIrql());
+ ASSERT(OldIrql <= APC_LEVEL);
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+ return STATUS_PAGE_FAULT_TRANSITION;
+ }
}
//
@@ -469,7 +584,7 @@
IN PVOID TrapInformation)
{
KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
- PMMPTE PointerPte, ProtoPte;
+ PMMPTE PointerPte, ProtoPte = NULL;
PMMPDE PointerPde;
MMPTE TempPte;
PETHREAD CurrentThread;
@@ -645,9 +760,6 @@
/* Check one kind of prototype PTE */
if (TempPte.u.Soft.Prototype)
{
- /* The one used for protected pool... */
- ASSERT(MmProtectFreedNonPagedPool == TRUE);
-
/* Make sure protected pool is on, and that this is a pool address */
if ((MmProtectFreedNonPagedPool) &&
(((Address >= MmNonPagedPoolStart) &&
@@ -663,12 +775,41 @@
Mode,
4);
}
+
+ /* Get the prototype PTE! */
+ ProtoPte = MiProtoPteToPte(&TempPte);
}
//
// We don't implement transition PTEs
//
ASSERT(TempPte.u.Soft.Transition == 0);
+
+ /* Check for no-access PTE */
+ if (TempPte.u.Soft.Protection == MM_NOACCESS)
+ {
+ /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
+ KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
+ (ULONG_PTR)Address,
+ StoreInstruction,
+ (ULONG_PTR)TrapInformation,
+ 1);
+ }
+
+ /* Check for demand page */
+ if ((StoreInstruction) && !(ProtoPte) && !(TempPte.u.Hard.Valid))
+ {
+ /* Get the protection code */
+ if (!(TempPte.u.Soft.Protection & MM_READWRITE))
+ {
+ /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
+ KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
+ (ULONG_PTR)Address,
+ TempPte.u.Long,
+ (ULONG_PTR)TrapInformation,
+ 14);
+ }
+ }
//
// Now do the real fault handling
@@ -676,7 +817,7 @@
Status = MiDispatchFault(StoreInstruction,
Address,
PointerPte,
- NULL,
+ ProtoPte,
FALSE,
NULL,
TrapInformation,
Author: sir_richard
Date: Mon Oct 4 18:34:41 2010
New Revision: 48977
URL: http://svn.reactos.org/svn/reactos?rev=48977&view=rev
Log:
[NTOS]: Define MI_MAKE_PROTOTYPE_PTE macro to make a real prototype PTE from a PTE. Define counter-part MiProtoPteToPte to recover the true PTE from a given Prototype PTE.
[NTOS]: Define MI_PTE_LOOKUP_NEEDED instead of using 0xFFFF. The name was found in checked build assertion strings.
[NTOS]: Add MM_VIEW (used for System-mapped Section Views) and MM_SESSSION (used to define the system/session view mappings) structure definitions.
Modified:
trunk/reactos/ntoskrnl/mm/ARM3/i386/init.c
trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
Modified: trunk/reactos/ntoskrnl/mm/ARM3/i386/init.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/i386/init…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/i386/init.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/i386/init.c [iso-8859-1] Mon Oct 4 18:34:41 2010
@@ -26,7 +26,7 @@
MMPDE DemandZeroPde = {.u.Long = (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS)};
/* Template PTE for prototype page */
-MMPTE PrototypePte = {.u.Long = (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS) | PTE_PROTOTYPE | 0xFFFFF000};
+MMPTE PrototypePte = {.u.Long = (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS) | PTE_PROTOTYPE | (MI_PTE_LOOKUP_NEEDED << PAGE_SHIFT)};
/* PRIVATE FUNCTIONS **********************************************************/
Modified: trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/miarm.h?r…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] Mon Oct 4 18:34:41 2010
@@ -232,6 +232,18 @@
#define MI_GET_PAGE_COLOR(x) ((x) & MmSecondaryColorMask)
#define MI_GET_NEXT_COLOR(x) (MI_GET_PAGE_COLOR(++MmSystemPageColor))
#define MI_GET_NEXT_PROCESS_COLOR(x) (MI_GET_PAGE_COLOR(++(x)->NextPageColor))
+
+//
+// Decodes a Prototype PTE into the underlying PTE
+//
+#define MiProtoPteToPte(x) \
+ (PMMPTE)((ULONG_PTR)MmPagedPoolStart + \
+ ((x)->u.Proto.ProtoAddressHigh | (x)->u.Proto.ProtoAddressLow))
+
+//
+// Prototype PTEs that don't yet have a pagefile association
+//
+#define MI_PTE_LOOKUP_NEEDED 0xFFFFF
//
// FIXFIX: These should go in ex.h after the pool merge
@@ -358,6 +370,25 @@
PFN_NUMBER StartFrame;
PFN_NUMBER LastFrame;
} MI_LARGE_PAGE_RANGES, *PMI_LARGE_PAGE_RANGES;
+
+typedef struct _MMVIEW
+{
+ ULONG_PTR Entry;
+ PCONTROL_AREA ControlArea;
+} MMVIEW, *PMMVIEW;
+
+typedef struct _MMSESSION
+{
+ KGUARDED_MUTEX SystemSpaceViewLock;
+ PKGUARDED_MUTEX SystemSpaceViewLockPointer;
+ PCHAR SystemSpaceViewStart;
+ PMMVIEW SystemSpaceViewTable;
+ ULONG SystemSpaceHashSize;
+ ULONG SystemSpaceHashEntries;
+ ULONG SystemSpaceHashKey;
+ ULONG BitmapFailures;
+ PRTL_BITMAP SystemSpaceBitMap;
+} MMSESSION, *PMMSESSION;
extern MMPTE HyperTemplatePte;
extern MMPDE ValidKernelPde;
@@ -565,6 +596,31 @@
}
//
+// Builds a Prototype PTE for the address of the PTE
+//
+FORCEINLINE
+VOID
+MI_MAKE_PROTOTYPE_PTE(IN PMMPTE NewPte,
+ IN PMMPTE PointerPte)
+{
+ ULONG_PTR Offset;
+
+ /* Mark this as a prototype */
+ NewPte->u.Long = 0;
+ NewPte->u.Proto.Prototype = 1;
+
+ /*
+ * Prototype PTEs are only valid in paged pool by design, this little trick
+ * lets us only use 28 bits for the adress of the PTE
+ */
+ Offset = (ULONG_PTR)PointerPte - (ULONG_PTR)MmPagedPoolStart;
+
+ /* 7 bits go in the "low", and the other 21 bits go in the "high" */
+ NewPte->u.Proto.ProtoAddressLow = Offset & 0x7F;
+ NewPte->u.Proto.ProtoAddressHigh = Offset & 0xFFFFF80;
+}
+
+//
// Returns if the page is physically resident (ie: a large page)
// FIXFIX: CISC/x86 only?
//
@@ -1107,6 +1163,12 @@
IN PMMADDRESS_NODE Node
);
+BOOLEAN
+NTAPI
+MiInitializeSystemSpaceMap(
+ IN PVOID InputSession OPTIONAL
+);
+
//
// MiRemoveZeroPage will use inline code to zero out the page manually if only
// free pages are available. In some scenarios, we don't/can't run that piece of
Author: sir_richard
Date: Mon Oct 4 18:22:50 2010
New Revision: 48976
URL: http://svn.reactos.org/svn/reactos?rev=48976&view=rev
Log:
[NTOS]: Go ahead and now fill out the OriginalPte field for PFNs initialized with MiInitializePfn(ForOtherProcess). They should only belong to ARM3 so they'll never have SwapEntry/RMAP associated with them. This functionality is important for future Prototype PTE support, among other things, as it lets us get the original PTE value written for a given PFN entry.
Modified:
trunk/reactos/ntoskrnl/mm/ARM3/pfnlist.c
Modified: trunk/reactos/ntoskrnl/mm/ARM3/pfnlist.c
URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/pfnlist.c…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/pfnlist.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/pfnlist.c [iso-8859-1] Mon Oct 4 18:22:50 2010
@@ -713,6 +713,16 @@
{
/* Only valid from MmCreateProcessAddressSpace path */
ASSERT(PsGetCurrentProcess()->Vm.WorkingSetSize == 0);
+
+ /* Make this a demand zero PTE */
+ MI_MAKE_SOFTWARE_PTE(&Pfn1->OriginalPte, MM_READWRITE);
+ }
+ else
+ {
+ /* Copy the PTE data */
+ Pfn1->OriginalPte = *PointerPte;
+ ASSERT(!((Pfn1->OriginalPte.u.Soft.Prototype == 0) &&
+ (Pfn1->OriginalPte.u.Soft.Transition == 1)));
}
/* Otherwise this is a fresh page -- set it up */
@@ -870,11 +880,9 @@
/* Setup the PTE */
Pfn1 = MiGetPfnEntry(PageFrameIndex);
Pfn1->PteAddress = PointerPte;
-
-#if 0 // When using ARM3 PFN
+
/* Make this a software PTE */
MI_MAKE_SOFTWARE_PTE(&Pfn1->OriginalPte, MM_READWRITE);
-#endif
/* Setup the page */
ASSERT(Pfn1->u3.e2.ReferenceCount == 0);