Author: ros-arm-bringup Date: Fri Oct 16 00:08:26 2009 New Revision: 43507
URL: http://svn.reactos.org/svn/reactos?rev=43507&view=rev Log: - Implement ARM3 page fault handling. - Paged pool PTEs are demand zero PTEs while the memory hasn't been accessed -- this is the only type of fault supported. - Because paged pool PDEs are also demand-paged, added code to handle demand paging of PDEs as well. - Also, because paged pool is non-resident, but can be accessed from any process, we need a mechanism to sync up the kernel's page directory with the per-process one, on demand. This is done at startup, but other processes may have paged in paged pool that another process knows nothing about when he faults. - Similar to the hack ReactOS Mm uses, but done properly. - This is what that shadow system page directory is finally being used for. - Assert if we get a user-mode fault, a transition fault, or a soft fault, since these shouldn't happen. - Disable APCs while dispatching faults, and pseudo-use the working set lock. - Assert if we get write errors on read-only pages, since we don't use those in ARM3 yet. - Assert if we have a paged out PTE, this shouldn't happen yet. - Enable test to see if we can touch a paged pool allocation.
Added: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c (with props) Modified: trunk/reactos/ntoskrnl/mm/mmfault.c trunk/reactos/ntoskrnl/mm/mminit.c trunk/reactos/ntoskrnl/ntoskrnl-generic.rbuild
Added: 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 (added) +++ trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] Fri Oct 16 00:08:26 2009 @@ -1,0 +1,391 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/mm/ARM3/pagfault.c + * PURPOSE: ARM Memory Manager Page Fault Handling + * PROGRAMMERS: ReactOS Portable Systems Group + */ + +/* INCLUDES *******************************************************************/ + +#include <ntoskrnl.h> +#define NDEBUG +#include <debug.h> + +#line 15 "ARM³::PAGFAULT" +#define MODULE_INVOLVED_IN_ARM3 +#include "../ARM3/miarm.h" + +/* GLOBALS ********************************************************************/ + +/* PRIVATE FUNCTIONS **********************************************************/ + +NTSTATUS +FASTCALL +MiCheckPdeForPagedPool(IN PVOID Address) +{ + PMMPTE PointerPde; + NTSTATUS Status = STATUS_SUCCESS; + + // + // Check if this is a fault while trying to access the page table itself + // + if ((Address >= (PVOID)MiAddressToPte(MmSystemRangeStart)) && + (Address < (PVOID)PTE_TOP)) + { + // + // Send a hint to the page fault handler that this is only a valid fault + // if we already detected this was access within the page table range + // + PointerPde = MiAddressToPte(Address); + Status = STATUS_WAIT_1; + } + else if (Address < MmSystemRangeStart) + { + // + // This is totally illegal + // + return STATUS_ACCESS_VIOLATION; + } + else + { + // + // Get the PDE for the address + // + PointerPde = MiAddressToPde(Address); + } + + // + // Check if it's not valid + // + if (PointerPde->u.Hard.Valid == 0) + { + // + // Copy it from our double-mapped system page directory + // + InterlockedExchangePte(PointerPde, + MmSystemPagePtes[((ULONG_PTR)PointerPde & + (PAGE_SIZE - 1)) / + sizeof(MMPTE)].u.Long); + } + + // + // Return status + // + return Status; +} + +NTSTATUS +NTAPI +MiResolveDemandZeroFault(IN PVOID Address, + IN PMMPTE PointerPte, + IN PEPROCESS Process, + IN KIRQL OldIrql) +{ + PFN_NUMBER PageFrameNumber; + MMPTE TempPte; + DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n", + Address, + Process); + + // + // Lock the PFN database + // + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + ASSERT(PointerPte->u.Hard.Valid == 0); + + // + // Get a page + // + PageFrameNumber = MmAllocPage(MC_PPOOL, 0); + DPRINT("New pool page: %lx\n", PageFrameNumber); + + // + // Release PFN lock + // + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + // + // Increment demand zero faults + // + InterlockedIncrement(&KeGetCurrentPrcb()->MmDemandZeroCount); + + // + // Build the PTE + // + TempPte = HyperTemplatePte; + TempPte.u.Hard.PageFrameNumber = PageFrameNumber; + *PointerPte = TempPte; + ASSERT(PointerPte->u.Hard.Valid == 1); + + // + // It's all good now + // + DPRINT("Paged pool page has now been paged in\n"); + return STATUS_PAGE_FAULT_DEMAND_ZERO; +} + +NTSTATUS +NTAPI +MiDispatchFault(IN BOOLEAN StoreInstruction, + IN PVOID Address, + IN PMMPTE PointerPte, + IN PMMPTE PrototypePte, + IN BOOLEAN Recursive, + IN PEPROCESS Process, + IN PVOID TrapInformation, + IN PVOID Vad) +{ + MMPTE TempPte; + KIRQL OldIrql; + NTSTATUS Status; + DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n", + Address, + Process); + + // + // Make sure APCs are off and we're not at dispatch + // + OldIrql = KeGetCurrentIrql (); + ASSERT(OldIrql <= APC_LEVEL); + ASSERT(KeAreAllApcsDisabled () == TRUE); + + // + // Grab a copy of the PTE + // + TempPte = *PointerPte; + + // + // The PTE must be invalid, but not totally blank + // + ASSERT(TempPte.u.Hard.Valid == 0); + ASSERT(TempPte.u.Long != 0); + + // + // No prototype, transition or page file software PTEs in ARM3 yet + // + ASSERT(TempPte.u.Soft.Prototype == 0); + ASSERT(TempPte.u.Soft.Transition == 0); + ASSERT(TempPte.u.Soft.PageFileHigh == 0); + + // + // If we got this far, the PTE can only be a demand zero PTE, which is what + // we want. Go handle it! + // + Status = MiResolveDemandZeroFault(Address, + PointerPte, + Process, + -1); + if (NT_SUCCESS(Status)) + { + // + // Make sure we're returning in a sane state and pass the status down + // + ASSERT(OldIrql == KeGetCurrentIrql ()); + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + return Status; + } + + // + // Generate an access fault + // + return STATUS_ACCESS_VIOLATION; +} + +NTSTATUS +NTAPI +MmArmAccessFault(IN BOOLEAN StoreInstruction, + IN PVOID Address, + IN KPROCESSOR_MODE Mode, + IN PVOID TrapInformation) +{ + KIRQL OldIrql = KeGetCurrentIrql(), LockIrql; + PMMPTE PointerPde, PointerPte; + MMPTE TempPte; + PETHREAD CurrentThread; + NTSTATUS Status; + DPRINT("ARM3 FAULT AT: %p\n", Address); + + // + // Get the PTE and PDE + // + PointerPte = MiAddressToPte(Address); + PointerPde = MiAddressToPde(Address); + + // + // Check for dispatch-level snafu + // + if (OldIrql > APC_LEVEL) + { + // + // There are some special cases where this is okay, but not in ARM3 yet + // + DbgPrint("MM:***PAGE FAULT AT IRQL > 1 Va %p, IRQL %lx\n", + Address, + OldIrql); + ASSERT(OldIrql <= APC_LEVEL); + } + + // + // Check for kernel fault + // + if (Address >= MmSystemRangeStart) + { + // + // What are you even DOING here? + // + if (Mode == UserMode) return STATUS_ACCESS_VIOLATION; + + // + // Is the PDE valid? + // + if (!PointerPde->u.Hard.Valid == 0) + { + // + // Debug spew (eww!) + // + DPRINT("Invalid PDE\n"); + + // + // Handle mapping in "Special" PDE directoreis + // + MiCheckPdeForPagedPool(Address); + + // + // Now we SHOULD be good + // + if (PointerPde->u.Hard.Valid == 0) + { + // + // FIXFIX: Do the S-LIST hack + // + + // + // Kill the system + // + KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA, + (ULONG_PTR)Address, + StoreInstruction, + (ULONG_PTR)TrapInformation, + 2); + } + } + + // + // The PDE is valid, so read the PTE + // + TempPte = *PointerPte; + if (TempPte.u.Hard.Valid == 1) + { + // + // Only two things can go wrong here: + // Executing NX page (we couldn't care less) + // Writing to a read-only page (the stuff ARM3 works with is write, + // so again, moot point). + // + if (StoreInstruction) + { + DPRINT1("Should NEVER happen on ARM3!!!\n"); + return STATUS_ACCESS_VIOLATION; + } + + // + // Otherwise, the PDE was probably invalid, and all is good now + // + return STATUS_SUCCESS; + } + + // + // Check for a fault on the page table or hyperspace itself + // FIXME: Use MmHyperSpaceEnd + // + if ((Address >= (PVOID)PTE_BASE) && (Address <= (PVOID)0xC0800000)) + { + // + // This might happen...not sure yet + // + DPRINT1("FAULT ON PAGE TABLES!\n"); + return STATUS_ACCESS_VIOLATION; + } + + // + // Now we must raise to APC_LEVEL and mark the thread as owner + // We don't actually implement a working set pushlock, so this is only + // for internal consistency (and blocking APCs) + // + KeRaiseIrql(APC_LEVEL, &LockIrql); + CurrentThread = PsGetCurrentThread(); + KeEnterGuardedRegion(); + ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) && + (CurrentThread->OwnsSystemWorkingSetShared == 0)); + CurrentThread->OwnsSystemWorkingSetExclusive = 1; + + // + // Re-read PTE now that the IRQL has been raised + // + TempPte = *PointerPte; + if (TempPte.u.Hard.Valid == 1) + { + // + // Only two things can go wrong here: + // Executing NX page (we couldn't care less) + // Writing to a read-only page (the stuff ARM3 works with is write, + // so again, moot point. + // + if (StoreInstruction) + { + DPRINT1("Should NEVER happen on ARM3!!!\n"); + return STATUS_ACCESS_VIOLATION; + } + + // + // Otherwise, the PDE was probably invalid, and all is good now + // + return STATUS_SUCCESS; + } + + // + // We don't implement prototype PTEs + // + ASSERT(TempPte.u.Soft.Prototype == 0); + + // + // We don't implement transition PTEs + // + ASSERT(TempPte.u.Soft.Transition == 0); + + // + // Now do the real fault handling + // + Status = MiDispatchFault(StoreInstruction, + Address, + PointerPte, + NULL, + FALSE, + NULL, + TrapInformation, + NULL); + + // + // Re-enable APCs + // + ASSERT(KeAreAllApcsDisabled() == TRUE); + CurrentThread->OwnsSystemWorkingSetExclusive = 0; + KeLeaveGuardedRegion(); + KeLowerIrql(LockIrql); + + // + // We are done! + // + DPRINT("Fault resolved with status: %lx\n", Status); + return Status; + } + + // + // DIE DIE DIE + // + DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n"); + return STATUS_ACCESS_VIOLATION; +} + +/* EOF */
Propchange: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c ------------------------------------------------------------------------------ svn:eol-style = native
Propchange: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c ------------------------------------------------------------------------------ svn:mime-type = text/plain
Modified: trunk/reactos/ntoskrnl/mm/mmfault.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/mmfault.c?rev=4... ============================================================================== --- trunk/reactos/ntoskrnl/mm/mmfault.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/mmfault.c [iso-8859-1] Fri Oct 16 00:08:26 2009 @@ -283,9 +283,7 @@ // // Hand it off to more competent hands... // - UNIMPLEMENTED; - KeBugCheckEx(MEMORY_AREA_OWNED_BY_ARM3, Mode, (ULONG_PTR)Address, 0, 0); - //return MmArmAccessFault(StoreInstruction, Address, Mode, TrapInformation); + return MmArmAccessFault(StoreInstruction, Address, Mode, TrapInformation); }
/* Keep same old ReactOS Behaviour */
Modified: trunk/reactos/ntoskrnl/mm/mminit.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/mminit.c?rev=43... ============================================================================== --- trunk/reactos/ntoskrnl/mm/mminit.c [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/mm/mminit.c [iso-8859-1] Fri Oct 16 00:08:26 2009 @@ -403,12 +403,12 @@ // STEP 3: Allocate a page and touch it. // We should get an ARM3 page fault and it should handle the fault // - if (0) // NOT YET IMPLEMENTED + if (1) { PULONG Test;
Test = MiAllocatePoolPages(PagedPool, PAGE_SIZE); - DPRINT1("Value: %lx", *Test); + ASSERT(*Test == 0); MiFreePoolPages(Test); }
Modified: trunk/reactos/ntoskrnl/ntoskrnl-generic.rbuild URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ntoskrnl-generic.r... ============================================================================== --- trunk/reactos/ntoskrnl/ntoskrnl-generic.rbuild [iso-8859-1] (original) +++ trunk/reactos/ntoskrnl/ntoskrnl-generic.rbuild [iso-8859-1] Fri Oct 16 00:08:26 2009 @@ -392,6 +392,7 @@ <file>mdlsup.c</file> <file>mmsup.c</file> <file>ncache.c</file> + <file>pagfault.c</file> <file>pool.c</file> <file>procsup.c</file> <file>syspte.c</file>