Author: sir_richard Date: Mon Mar 26 07:41:47 2012 New Revision: 56233
URL: http://svn.reactos.org/svn/reactos?rev=56233&view=rev Log: [NTOS]: Add support for determining transition vs. demand zero faults (the former should not yet happen). [NTOS]: Add a function for removing transition pages from either the standby or modified page lists (not yet used).
Modified: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c trunk/reactos/ntoskrnl/mm/ARM3/pfnlist.c
Modified: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c... ============================================================================== --- trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] Mon Mar 26 07:41:47 2012 @@ -446,6 +446,151 @@
NTSTATUS NTAPI +MiResolveTransitionFault(IN PVOID FaultingAddress, + IN PMMPTE PointerPte, + IN PEPROCESS CurrentProcess, + IN KIRQL OldIrql, + OUT PVOID *InPageBlock) +{ + PFN_NUMBER PageFrameIndex; + PMMPFN Pfn1; + MMPTE TempPte; + PMMPTE PointerToPteForProtoPage; + USHORT NewRefCount; + DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName); + + /* Windowss does this check */ + ASSERT(*InPageBlock == NULL); + + /* ARM3 doesn't support this path */ + ASSERT(OldIrql != MM_NOIRQL); + + /* Capture the PTE and make sure it's in transition format */ + TempPte = *PointerPte; + ASSERT((TempPte.u.Soft.Valid == 0) && + (TempPte.u.Soft.Prototype == 0) && + (TempPte.u.Soft.Transition == 1)); + + /* Get the PFN and the PFN entry */ + PageFrameIndex = TempPte.u.Trans.PageFrameNumber; + DPRINT1("Transition PFN: %lx\n", PageFrameIndex); + Pfn1 = MiGetPfnEntry(PageFrameIndex); + + /* One more transition fault! */ + InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount); + + /* This is from ARM3 -- Windows normally handles this here */ + ASSERT(Pfn1->u4.InPageError == 0); + + /* Not supported in ARM3 */ + ASSERT(Pfn1->u3.e1.ReadInProgress == 0); + + /* Windows checks there's some free pages and this isn't an in-page error */ + ASSERT(MmAvailablePages >= 0); + ASSERT(Pfn1->u4.InPageError == 0); + + /* Was this a transition page in the valid list, or free/zero list? */ + if (Pfn1->u3.e1.PageLocation == ActiveAndValid) + { + /* All Windows does here is a bunch of sanity checks */ + DPRINT1("Transition in active list\n"); + ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) && + (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd))); + ASSERT(Pfn1->u2.ShareCount != 0); + ASSERT(Pfn1->u3.e2.ReferenceCount != 0); + } + else + { + /* Otherwise, the page is removed from its list */ + DPRINT1("Transition page in free/zero list\n"); + MiUnlinkPageFromList(Pfn1); + + /* Windows does these checks -- perhaps a macro? */ + ASSERT(Pfn1->u2.ShareCount == 0); + ASSERT(Pfn1->u2.ShareCount == 0); + ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); + + /* Check if this was a prototype PTE */ + if ((Pfn1->u3.e1.PrototypePte == 1) && + (Pfn1->OriginalPte.u.Soft.Prototype == 1)) + { + DPRINT1("Prototype floating page not yet supported\n"); + ASSERT(FALSE); + } + + /* FIXME: Update counter */ + + /* We must be the first reference */ + NewRefCount = InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount); + ASSERT(NewRefCount == 1); + } + + /* At this point, there should no longer be any in-page errors */ + ASSERT(Pfn1->u4.InPageError == 0); + + /* Check if this was a PFN with no more share references */ + if (Pfn1->u2.ShareCount == 0) + { + /* Windows checks for these... maybe a macro? */ + ASSERT(Pfn1->u3.e2.ReferenceCount != 0); + ASSERT(Pfn1->u2.ShareCount == 0); + + /* Was this the last active reference to it */ + DPRINT1("Page share count is zero\n"); + if (Pfn1->u3.e2.ReferenceCount == 1) + { + /* The page should be leaking somewhere on the free/zero list */ + DPRINT1("Page reference count is one\n"); + ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid); + if ((Pfn1->u3.e1.PrototypePte == 1) && + (Pfn1->OriginalPte.u.Soft.Prototype == 1)) + { + /* Do extra processing if it was a prototype page */ + DPRINT1("Prototype floating page not yet supported\n"); + ASSERT(FALSE); + } + + /* FIXME: Update counter */ + } + } + + /* Bump the share count and make the page valid */ + Pfn1->u2.ShareCount++; + Pfn1->u3.e1.PageLocation = ActiveAndValid; + + /* Prototype PTEs are in paged pool, which itself might be in transition */ + if (FaultingAddress >= MmSystemRangeStart) + { + /* Check if this is a paged pool PTE in transition state */ + PointerToPteForProtoPage = MiAddressToPte(PointerPte); + TempPte = *PointerToPteForProtoPage; + if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1)) + { + /* This isn't yet supported */ + DPRINT1("Double transition fault not yet supported\n"); + ASSERT(FALSE); + } + } + + /* Build the transition PTE -- maybe a macro? */ + ASSERT(PointerPte->u.Hard.Valid == 0); + ASSERT(PointerPte->u.Trans.Prototype == 0); + ASSERT(PointerPte->u.Trans.Transition == 1); + TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) | + (MmProtectToPteMask[PointerPte->u.Trans.Protection]) | + MiDetermineUserGlobalPteMask(PointerPte); + + /* FIXME: Set dirty bit */ + + /* Write the valid PTE */ + MI_WRITE_VALID_PTE(PointerPte, TempPte); + + /* Return success */ + return STATUS_PAGE_FAULT_TRANSITION; +} + +NTSTATUS +NTAPI MiResolveProtoPteFault(IN BOOLEAN StoreInstruction, IN PVOID Address, IN PMMPTE PointerPte, @@ -457,10 +602,11 @@ IN KIRQL OldIrql, IN PVOID TrapInformation) { - MMPTE TempPte; + MMPTE TempPte, PteContents; PMMPFN Pfn1; PFN_NUMBER PageFrameIndex; NTSTATUS Status; + PVOID InPageBlock = NULL;
/* Must be called with an invalid, prototype PTE, with the PFN lock held */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); @@ -497,19 +643,50 @@ 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); + /* Check for access rights on the PTE proper */ + PteContents = *PointerPte; + if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED) + { + if (!PteContents.u.Proto.ReadOnly) + { + /* FIXME: CHECK FOR ACCESS AND COW */ + } + } + else + { + /* FIXME: Should check for COW */ + } + + /* Check for clone PTEs */ + if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL); + + /* We don't support mapped files yet */ 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)); + + /* We might however have transition PTEs */ + if (TempPte.u.Soft.Transition == 1) + { + /* Resolve the transition fault */ + ASSERT(OldIrql != MM_NOIRQL); + Status = MiResolveTransitionFault(Address, + PointerProtoPte, + Process, + OldIrql, + &InPageBlock); + ASSERT(NT_SUCCESS(Status)); + } + else + { + /* We also don't support paged out pages */ + ASSERT(TempPte.u.Soft.PageFileHigh == 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);
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 Mar 26 07:41:47 2012 @@ -225,6 +225,124 @@ // MI_PFN_CURRENT_USAGE = MI_USAGE_NOT_SET; // memcpy(MI_PFN_CURRENT_PROCESS_NAME, "Not Set", 16); #endif +} + +VOID +NTAPI +MiUnlinkPageFromList(IN PMMPFN Pfn) +{ + PMMPFNLIST ListHead; + PFN_NUMBER OldFlink, OldBlink; + + /* Make sure the PFN lock is held */ + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* ARM3 should only call this for dead pages */ + ASSERT(Pfn->u3.e2.ReferenceCount == 0); + + /* Transition pages are supposed to be standby/modified/nowrite */ + ListHead = MmPageLocationList[Pfn->u3.e1.PageLocation]; + ASSERT(ListHead->ListName >= StandbyPageList); + + /* Check if this was standby, or modified */ + if (ListHead == &MmStandbyPageListHead) + { + /* Should not be a ROM page */ + ASSERT(Pfn->u3.e1.Rom == 0); + + /* Get the exact list */ + ListHead = &MmStandbyPageListByPriority[Pfn->u4.Priority]; + + /* See if we hit any thresholds */ + if (MmAvailablePages == MmHighMemoryThreshold) + { + /* Clear the high memory event */ + KeClearEvent(MiHighMemoryEvent); + } + else if (MmAvailablePages == MmLowMemoryThreshold) + { + /* Signal the low memory event */ + KeSetEvent(MiLowMemoryEvent, 0, FALSE); + } + + /* Decrease transition page counter */ + ASSERT(Pfn->u3.e1.PrototypePte == 1); /* Only supported ARM3 case */ + MmTransitionSharedPages--; + + /* One less page */ + if (--MmAvailablePages < MmMinimumFreePages) + { + /* FIXME: Should wake up the MPW and working set manager, if we had one */ + DPRINT1("Running low on pages: %d remaining\n", MmAvailablePages); + + /* Call RosMm and see if it can release any pages for us */ + MmRebalanceMemoryConsumers(); + } + } + else if (ListHead == &MmModifiedPageListHead) + { + /* Only shared memory (page-file backed) modified pages are supported */ + ASSERT(Pfn->OriginalPte.u.Soft.Prototype == 0); + + /* Decrement the counters */ + ListHead->Total--; + MmTotalPagesForPagingFile--; + + /* Pick the correct colored list */ + ListHead = &MmModifiedPageListByColor[0]; + + /* Decrease transition page counter */ + ASSERT(Pfn->u3.e1.PrototypePte == 1); /* Only supported ARM3 case */ + MmTransitionSharedPages--; + } + else if (ListHead == &MmModifiedNoWritePageListHead) + { + /* List not yet supported */ + ASSERT(FALSE); + } + + /* Nothing should be in progress and the list should not be empty */ + ASSERT(Pfn->u3.e1.WriteInProgress == 0); + ASSERT(Pfn->u3.e1.ReadInProgress == 0); + ASSERT(ListHead->Total != 0); + + /* Get the forward and back pointers */ + OldFlink = Pfn->u1.Flink; + OldBlink = Pfn->u2.Blink; + + /* Check if the next entry is the list head */ + if (OldFlink != LIST_HEAD) + { + /* It is not, so set the backlink of the actual entry, to our backlink */ + MI_PFN_ELEMENT(OldFlink)->u2.Blink = OldBlink; + } + else + { + /* Set the list head's backlink instead */ + ListHead->Blink = OldBlink; + } + + /* Check if the back entry is the list head */ + if (OldBlink != LIST_HEAD) + { + /* It is not, so set the backlink of the actual entry, to our backlink */ + MI_PFN_ELEMENT(OldBlink)->u1.Flink = OldFlink; + } + else + { + /* Set the list head's backlink instead */ + ListHead->Flink = OldFlink; + } + + /* ReactOS Hack */ + Pfn->OriginalPte.u.Long = 0; + + /* We are not on a list anymore */ + Pfn->u1.Flink = Pfn->u2.Blink = 0; + ASSERT_LIST_INVARIANT(ListHead); + + /* Remove one entry from the list */ + ListHead->Total--; }
PFN_NUMBER