mirror of
https://github.com/reactos/reactos.git
synced 2024-09-22 10:37:10 +00:00
406dfdbc87
Sync trunk (rr49606) svn path=/branches/cmake-bringup/; revision=49607
767 lines
20 KiB
C
767 lines
20 KiB
C
/*
|
|
* Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/mm/section/fault.c
|
|
* PURPOSE: Consolidate fault handlers for sections
|
|
*
|
|
* PROGRAMMERS: Arty
|
|
* Rex Jolliff
|
|
* David Welch
|
|
* Eric Kohl
|
|
* Emanuele Aliberti
|
|
* Eugene Ingerman
|
|
* Casper Hornstrup
|
|
* KJK::Hyperion
|
|
* Guido de Jong
|
|
* Ge van Geldorp
|
|
* Royce Mitchell III
|
|
* Filip Navara
|
|
* Aleksey Bragin
|
|
* Jason Filby
|
|
* Thomas Weidenmueller
|
|
* Gunnar Andre' Dalsnes
|
|
* Mike Nordell
|
|
* Alex Ionescu
|
|
* Gregor Anich
|
|
* Steven Edwards
|
|
* Herve Poussineau
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#include "newmm.h"
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#define DPRINTC DPRINT
|
|
|
|
extern KEVENT MmWaitPageEvent;
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmNotPresentFaultCachePage
|
|
(PMMSUPPORT AddressSpace,
|
|
MEMORY_AREA* MemoryArea,
|
|
PVOID Address,
|
|
BOOLEAN Locked,
|
|
PMM_REQUIRED_RESOURCES Required)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID PAddress;
|
|
ULONG Consumer;
|
|
PMM_CACHE_SECTION_SEGMENT Segment;
|
|
LARGE_INTEGER FileOffset, TotalOffset;
|
|
ULONG Entry;
|
|
ULONG Attributes;
|
|
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
|
|
|
|
DPRINT("Not Present: %p %p (%p-%p)\n", AddressSpace, Address, MemoryArea->StartingAddress, MemoryArea->EndingAddress);
|
|
|
|
/*
|
|
* There is a window between taking the page fault and locking the
|
|
* address space when another thread could load the page so we check
|
|
* that.
|
|
*/
|
|
if (MmIsPagePresent(Process, Address))
|
|
{
|
|
DPRINT("Done\n");
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
|
|
TotalOffset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress;
|
|
|
|
Segment = MemoryArea->Data.CacheData.Segment;
|
|
|
|
TotalOffset.QuadPart += MemoryArea->Data.CacheData.ViewOffset.QuadPart;
|
|
FileOffset = TotalOffset;
|
|
|
|
//Consumer = (Segment->Flags & MM_DATAFILE_SEGMENT) ? MC_CACHE : MC_USER;
|
|
Consumer = MC_CACHE;
|
|
|
|
if (Segment->FileObject)
|
|
{
|
|
DPRINT("FileName %wZ\n", &Segment->FileObject->FileName);
|
|
}
|
|
|
|
DPRINT("Total Offset %08x%08x\n", TotalOffset.HighPart, TotalOffset.LowPart);
|
|
|
|
/*
|
|
* Lock the segment
|
|
*/
|
|
MmLockCacheSectionSegment(Segment);
|
|
|
|
/*
|
|
* Get the entry corresponding to the offset within the section
|
|
*/
|
|
Entry = MiGetPageEntryCacheSectionSegment(Segment, &TotalOffset);
|
|
|
|
Attributes = PAGE_READONLY;
|
|
|
|
if (Required->State && Required->Page[0])
|
|
{
|
|
DPRINT("Have file and page, set page %x in section @ %x #\n", Required->Page[0], TotalOffset.LowPart);
|
|
|
|
if (Required->SwapEntry)
|
|
MmSetSavedSwapEntryPage(Required->Page[0], Required->SwapEntry);
|
|
|
|
if (Required->State & 2)
|
|
{
|
|
DPRINT("Set in section @ %x\n", TotalOffset.LowPart);
|
|
Status = MiSetPageEntryCacheSectionSegment
|
|
(Segment, &TotalOffset, Entry = MAKE_PFN_SSE(Required->Page[0]));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
|
|
}
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
MiSetPageEvent(Process, Address);
|
|
DPRINT("Status %x\n", Status);
|
|
return STATUS_MM_RESTART_OPERATION;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Set %x in address space @ %x\n", Required->Page[0], Address);
|
|
Status = MmCreateVirtualMapping(Process, Address, Attributes, Required->Page, 1);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
MmInsertRmap(Required->Page[0], Process, Address);
|
|
}
|
|
else
|
|
{
|
|
// Drop the reference for our address space ...
|
|
MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
|
|
}
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
DPRINTC("XXX Set Event %x\n", Status);
|
|
MiSetPageEvent(Process, Address);
|
|
DPRINT("Status %x\n", Status);
|
|
return Status;
|
|
}
|
|
}
|
|
else if (Entry)
|
|
{
|
|
PFN_NUMBER Page = PFN_FROM_SSE(Entry);
|
|
DPRINT("Take reference to page %x #\n", Page);
|
|
|
|
MmReferencePage(Page);
|
|
|
|
Status = MmCreateVirtualMapping(Process, Address, Attributes, &Page, 1);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
MmInsertRmap(Page, Process, Address);
|
|
}
|
|
DPRINT("XXX Set Event %x\n", Status);
|
|
MiSetPageEvent(Process, Address);
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
DPRINT("Status %x\n", Status);
|
|
return Status;
|
|
}
|
|
else
|
|
{
|
|
DPRINT("Get page into section\n");
|
|
/*
|
|
* If the entry is zero (and it can't change because we have
|
|
* locked the segment) then we need to load the page.
|
|
*/
|
|
//DPRINT1("Read from file %08x %wZ\n", FileOffset.LowPart, &Section->FileObject->FileName);
|
|
Required->State = 2;
|
|
Required->Context = Segment->FileObject;
|
|
Required->Consumer = Consumer;
|
|
Required->FileOffset = FileOffset;
|
|
Required->Amount = PAGE_SIZE;
|
|
Required->DoAcquisition = MiReadFilePage;
|
|
MiSetPageEntryCacheSectionSegment(Segment, &TotalOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
ASSERT(FALSE);
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiCopyPageToPage(PFN_NUMBER DestPage, PFN_NUMBER SrcPage)
|
|
{
|
|
PEPROCESS Process;
|
|
KIRQL Irql, Irql2;
|
|
PVOID TempAddress, TempSource;
|
|
|
|
Process = PsGetCurrentProcess();
|
|
TempAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
|
|
if (TempAddress == NULL)
|
|
{
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
TempSource = MiMapPageInHyperSpace(Process, SrcPage, &Irql2);
|
|
if (!TempSource) {
|
|
MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
memcpy(TempAddress, TempSource, PAGE_SIZE);
|
|
|
|
MiUnmapPageInHyperSpace(Process, TempSource, Irql2);
|
|
MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiCowCacheSectionPage
|
|
(PMMSUPPORT AddressSpace,
|
|
PMEMORY_AREA MemoryArea,
|
|
PVOID Address,
|
|
BOOLEAN Locked,
|
|
PMM_REQUIRED_RESOURCES Required)
|
|
{
|
|
PMM_CACHE_SECTION_SEGMENT Segment;
|
|
PFN_NUMBER NewPage, OldPage;
|
|
NTSTATUS Status;
|
|
PVOID PAddress;
|
|
LARGE_INTEGER Offset;
|
|
PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
|
|
|
|
DPRINT("MmAccessFaultSectionView(%x, %x, %x, %x)\n", AddressSpace, MemoryArea, Address, Locked);
|
|
|
|
Segment = MemoryArea->Data.CacheData.Segment;
|
|
|
|
/*
|
|
* Lock the segment
|
|
*/
|
|
MmLockCacheSectionSegment(Segment);
|
|
|
|
/*
|
|
* Find the offset of the page
|
|
*/
|
|
PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
|
|
Offset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress +
|
|
MemoryArea->Data.CacheData.ViewOffset.QuadPart;
|
|
|
|
#if 0 // XXX Cache sections are not CoW. For now, treat all access violations this way.
|
|
if ((!Segment->WriteCopy &&
|
|
!MemoryArea->Data.CacheData.WriteCopyView) ||
|
|
Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
|
|
#endif
|
|
{
|
|
#if 0 // XXX Cache sections don't have regions at present, which streamlines things
|
|
if (Region->Protect == PAGE_READWRITE ||
|
|
Region->Protect == PAGE_EXECUTE_READWRITE)
|
|
#endif
|
|
{
|
|
DPRINTC("setting non-cow page %x %x:%x offset %x (%x) to writable\n", Segment, Process, PAddress, Offset.u.LowPart, MmGetPfnForProcess(Process, Address));
|
|
if (Segment->FileObject)
|
|
{
|
|
DPRINTC("file %wZ\n", &Segment->FileObject->FileName);
|
|
}
|
|
ULONG Entry = MiGetPageEntryCacheSectionSegment(Segment, &Offset);
|
|
DPRINT("Entry %x\n", Entry);
|
|
if (Entry &&
|
|
!IS_SWAP_FROM_SSE(Entry) &&
|
|
PFN_FROM_SSE(Entry) == MmGetPfnForProcess(Process, Address)) {
|
|
MiSetPageEntryCacheSectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
|
|
}
|
|
MmSetPageProtect(Process, PAddress, PAGE_READWRITE);
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
DPRINT("Done\n");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#if 0
|
|
else
|
|
{
|
|
DPRINT("Not supposed to be writable\n");
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
return STATUS_ACCESS_VIOLATION;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (!Required->Page[0])
|
|
{
|
|
SWAPENTRY SwapEntry;
|
|
if (MmIsPageSwapEntry(Process, Address))
|
|
{
|
|
MmGetPageFileMapping(Process, Address, &SwapEntry);
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
if (SwapEntry == MM_WAIT_ENTRY)
|
|
return STATUS_SUCCESS + 1; // Wait ... somebody else is getting it right now
|
|
else
|
|
return STATUS_SUCCESS; // Nonwait swap entry ... handle elsewhere
|
|
}
|
|
Required->Page[1] = MmGetPfnForProcess(Process, Address);
|
|
Required->Consumer = MC_CACHE;
|
|
Required->Amount = 1;
|
|
Required->File = __FILE__;
|
|
Required->Line = __LINE__;
|
|
Required->DoAcquisition = MiGetOnePage;
|
|
MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
NewPage = Required->Page[0];
|
|
OldPage = Required->Page[1];
|
|
|
|
DPRINT("Allocated page %x\n", NewPage);
|
|
|
|
/*
|
|
* Unshare the old page.
|
|
*/
|
|
MmDeleteRmap(OldPage, Process, PAddress);
|
|
|
|
/*
|
|
* Copy the old page
|
|
*/
|
|
DPRINT("Copying\n");
|
|
MiCopyPageToPage(NewPage, OldPage);
|
|
|
|
/*
|
|
* Set the PTE to point to the new page
|
|
*/
|
|
Status = MmCreateVirtualMapping
|
|
(Process, Address, PAGE_READWRITE, &NewPage, 1);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
|
|
ASSERT(FALSE);
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
return(Status);
|
|
}
|
|
|
|
MmInsertRmap(NewPage, Process, PAddress);
|
|
MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
|
|
MmUnlockCacheSectionSegment(Segment);
|
|
|
|
DPRINT("Address 0x%.8X\n", Address);
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
KEVENT MmWaitPageEvent;
|
|
|
|
typedef struct _WORK_QUEUE_WITH_CONTEXT
|
|
{
|
|
WORK_QUEUE_ITEM WorkItem;
|
|
PMMSUPPORT AddressSpace;
|
|
PMEMORY_AREA MemoryArea;
|
|
PMM_REQUIRED_RESOURCES Required;
|
|
NTSTATUS Status;
|
|
KEVENT Wait;
|
|
AcquireResource DoAcquisition;
|
|
} WORK_QUEUE_WITH_CONTEXT, *PWORK_QUEUE_WITH_CONTEXT;
|
|
|
|
VOID
|
|
NTAPI
|
|
MmpFaultWorker
|
|
(PWORK_QUEUE_WITH_CONTEXT WorkItem)
|
|
{
|
|
DPRINT("Calling work\n");
|
|
WorkItem->Status =
|
|
WorkItem->Required->DoAcquisition
|
|
(WorkItem->AddressSpace,
|
|
WorkItem->MemoryArea,
|
|
WorkItem->Required);
|
|
DPRINT("Status %x\n", WorkItem->Status);
|
|
KeSetEvent(&WorkItem->Wait, IO_NO_INCREMENT, FALSE);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmpSectionAccessFaultInner
|
|
(KPROCESSOR_MODE Mode,
|
|
PMMSUPPORT AddressSpace,
|
|
ULONG_PTR Address,
|
|
BOOLEAN FromMdl,
|
|
PETHREAD Thread)
|
|
{
|
|
MEMORY_AREA* MemoryArea;
|
|
NTSTATUS Status;
|
|
BOOLEAN Locked = FromMdl;
|
|
MM_REQUIRED_RESOURCES Resources = { 0 };
|
|
|
|
DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
|
|
|
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
|
|
{
|
|
DPRINT1("Page fault at high IRQL was %d\n", KeGetCurrentIrql());
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
/*
|
|
* Find the memory area for the faulting address
|
|
*/
|
|
if (Address >= (ULONG_PTR)MmSystemRangeStart)
|
|
{
|
|
/*
|
|
* Check permissions
|
|
*/
|
|
if (Mode != KernelMode)
|
|
{
|
|
DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
|
|
return(STATUS_ACCESS_VIOLATION);
|
|
}
|
|
AddressSpace = MmGetKernelAddressSpace();
|
|
}
|
|
else
|
|
{
|
|
AddressSpace = &PsGetCurrentProcess()->Vm;
|
|
}
|
|
|
|
if (!FromMdl)
|
|
{
|
|
MmLockAddressSpace(AddressSpace);
|
|
}
|
|
|
|
do
|
|
{
|
|
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
|
|
if (MemoryArea == NULL ||
|
|
MemoryArea->DeleteInProgress)
|
|
{
|
|
if (!FromMdl)
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
}
|
|
DPRINT("Address: %x\n", Address);
|
|
return (STATUS_ACCESS_VIOLATION);
|
|
}
|
|
|
|
DPRINT
|
|
("Type %x (%x -> %x)\n",
|
|
MemoryArea->Type,
|
|
MemoryArea->StartingAddress,
|
|
MemoryArea->EndingAddress);
|
|
|
|
Resources.DoAcquisition = NULL;
|
|
|
|
// Note: fault handlers are called with address space locked
|
|
// We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
|
|
Status = MiCowCacheSectionPage
|
|
(AddressSpace, MemoryArea, (PVOID)Address, Locked, &Resources);
|
|
|
|
if (!FromMdl)
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS + 1)
|
|
{
|
|
// Wait page ...
|
|
DPRINT("Waiting for %x\n", Address);
|
|
MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
|
|
DPRINT("Restarting fault %x\n", Address);
|
|
Status = STATUS_MM_RESTART_OPERATION;
|
|
}
|
|
else if (Status == STATUS_MM_RESTART_OPERATION)
|
|
{
|
|
// Clean slate
|
|
RtlZeroMemory(&Resources, sizeof(Resources));
|
|
}
|
|
else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
if (Thread->ActiveFaultCount > 0)
|
|
{
|
|
WORK_QUEUE_WITH_CONTEXT Context = { };
|
|
DPRINT("Already fault handling ... going to work item (%x)\n", Address);
|
|
Context.AddressSpace = AddressSpace;
|
|
Context.MemoryArea = MemoryArea;
|
|
Context.Required = &Resources;
|
|
KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
|
|
ExInitializeWorkItem(&Context.WorkItem, (PWORKER_THREAD_ROUTINE)MmpFaultWorker, &Context);
|
|
DPRINT("Queue work item\n");
|
|
ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
|
|
DPRINT("Wait\n");
|
|
KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
|
|
Status = Context.Status;
|
|
DPRINT("Status %x\n", Status);
|
|
}
|
|
else
|
|
{
|
|
Status = Resources.DoAcquisition(AddressSpace, MemoryArea, &Resources);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = STATUS_MM_RESTART_OPERATION;
|
|
}
|
|
}
|
|
|
|
if (!FromMdl)
|
|
{
|
|
MmLockAddressSpace(AddressSpace);
|
|
}
|
|
}
|
|
while (Status == STATUS_MM_RESTART_OPERATION);
|
|
|
|
if (!NT_SUCCESS(Status) && MemoryArea->Type == 1)
|
|
{
|
|
DPRINT1("Completed page fault handling %x %x\n", Address, Status);
|
|
DPRINT1
|
|
("Type %x (%x -> %x)\n",
|
|
MemoryArea->Type,
|
|
MemoryArea->StartingAddress,
|
|
MemoryArea->EndingAddress);
|
|
}
|
|
|
|
if (!FromMdl)
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmAccessFaultCacheSection
|
|
(KPROCESSOR_MODE Mode,
|
|
ULONG_PTR Address,
|
|
BOOLEAN FromMdl)
|
|
{
|
|
PETHREAD Thread;
|
|
PMMSUPPORT AddressSpace;
|
|
NTSTATUS Status;
|
|
|
|
DPRINT("MmpAccessFault(Mode %d, Address %x)\n", Mode, Address);
|
|
|
|
Thread = PsGetCurrentThread();
|
|
|
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
|
|
{
|
|
DPRINT1("Page fault at high IRQL %d, address %x\n", KeGetCurrentIrql(), Address);
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
/*
|
|
* Find the memory area for the faulting address
|
|
*/
|
|
if (Address >= (ULONG_PTR)MmSystemRangeStart)
|
|
{
|
|
/*
|
|
* Check permissions
|
|
*/
|
|
if (Mode != KernelMode)
|
|
{
|
|
DPRINT1("Address: %x:%x\n", PsGetCurrentProcess(), Address);
|
|
return(STATUS_ACCESS_VIOLATION);
|
|
}
|
|
AddressSpace = MmGetKernelAddressSpace();
|
|
}
|
|
else
|
|
{
|
|
AddressSpace = &PsGetCurrentProcess()->Vm;
|
|
}
|
|
|
|
Thread->ActiveFaultCount++;
|
|
Status = MmpSectionAccessFaultInner(Mode, AddressSpace, Address, FromMdl, Thread);
|
|
Thread->ActiveFaultCount--;
|
|
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmNotPresentFaultCacheSectionInner
|
|
(KPROCESSOR_MODE Mode,
|
|
PMMSUPPORT AddressSpace,
|
|
ULONG_PTR Address,
|
|
BOOLEAN FromMdl,
|
|
PETHREAD Thread)
|
|
{
|
|
BOOLEAN Locked = FromMdl;
|
|
PMEMORY_AREA MemoryArea;
|
|
MM_REQUIRED_RESOURCES Resources = { 0 };
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (!FromMdl)
|
|
{
|
|
MmLockAddressSpace(AddressSpace);
|
|
}
|
|
|
|
/*
|
|
* Call the memory area specific fault handler
|
|
*/
|
|
do
|
|
{
|
|
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
|
|
if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
|
|
{
|
|
Status = STATUS_ACCESS_VIOLATION;
|
|
if (MemoryArea)
|
|
{
|
|
DPRINT1("Type %x DIP %x\n", MemoryArea->Type, MemoryArea->DeleteInProgress);
|
|
}
|
|
else
|
|
{
|
|
DPRINT1("No memory area\n");
|
|
}
|
|
DPRINT1("Process %x, Address %x\n", MmGetAddressSpaceOwner(AddressSpace), Address);
|
|
break;
|
|
}
|
|
|
|
DPRINTC
|
|
("Type %x (%x -> %x -> %x) in %x\n",
|
|
MemoryArea->Type,
|
|
MemoryArea->StartingAddress,
|
|
Address,
|
|
MemoryArea->EndingAddress,
|
|
PsGetCurrentThread());
|
|
|
|
Resources.DoAcquisition = NULL;
|
|
|
|
// Note: fault handlers are called with address space locked
|
|
// We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
|
|
|
|
Status = MmNotPresentFaultCachePage
|
|
(AddressSpace, MemoryArea, (PVOID)Address, Locked, &Resources);
|
|
|
|
if (!FromMdl)
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
; // Nothing
|
|
}
|
|
else if (Status == STATUS_SUCCESS + 1)
|
|
{
|
|
// Wait page ...
|
|
DPRINT("Waiting for %x\n", Address);
|
|
MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
|
|
DPRINT("Done waiting for %x\n", Address);
|
|
Status = STATUS_MM_RESTART_OPERATION;
|
|
}
|
|
else if (Status == STATUS_MM_RESTART_OPERATION)
|
|
{
|
|
// Clean slate
|
|
DPRINT("Clear resource\n");
|
|
RtlZeroMemory(&Resources, sizeof(Resources));
|
|
}
|
|
else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
|
|
{
|
|
if (Thread->ActiveFaultCount > 1)
|
|
{
|
|
WORK_QUEUE_WITH_CONTEXT Context = { };
|
|
DPRINTC("Already fault handling ... going to work item (%x)\n", Address);
|
|
Context.AddressSpace = AddressSpace;
|
|
Context.MemoryArea = MemoryArea;
|
|
Context.Required = &Resources;
|
|
KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
|
|
ExInitializeWorkItem(&Context.WorkItem, (PWORKER_THREAD_ROUTINE)MmpFaultWorker, &Context);
|
|
DPRINT("Queue work item\n");
|
|
ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
|
|
DPRINT("Wait\n");
|
|
KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
|
|
Status = Context.Status;
|
|
DPRINTC("Status %x\n", Status);
|
|
}
|
|
else
|
|
{
|
|
DPRINT("DoAcquisition %x\n", Resources.DoAcquisition);
|
|
Status = Resources.DoAcquisition
|
|
(AddressSpace, MemoryArea, &Resources);
|
|
DPRINT("DoAcquisition %x -> %x\n", Resources.DoAcquisition, Status);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = STATUS_MM_RESTART_OPERATION;
|
|
}
|
|
}
|
|
else if (NT_SUCCESS(Status))
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
if (!FromMdl)
|
|
{
|
|
MmLockAddressSpace(AddressSpace);
|
|
}
|
|
}
|
|
while (Status == STATUS_MM_RESTART_OPERATION);
|
|
|
|
DPRINTC("Completed page fault handling: %x:%x %x\n", MmGetAddressSpaceOwner(AddressSpace), Address, Status);
|
|
if (!FromMdl)
|
|
{
|
|
MmUnlockAddressSpace(AddressSpace);
|
|
}
|
|
|
|
MiSetPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
|
|
DPRINT("Done %x\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MmNotPresentFaultCacheSection
|
|
(KPROCESSOR_MODE Mode,
|
|
ULONG_PTR Address,
|
|
BOOLEAN FromMdl)
|
|
{
|
|
PETHREAD Thread;
|
|
PMMSUPPORT AddressSpace;
|
|
NTSTATUS Status;
|
|
|
|
Address &= ~(PAGE_SIZE - 1);
|
|
DPRINT("MmNotPresentFault(Mode %d, Address %x)\n", Mode, Address);
|
|
|
|
Thread = PsGetCurrentThread();
|
|
|
|
if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
|
|
{
|
|
DPRINT1("Page fault at high IRQL %d, address %x\n", KeGetCurrentIrql(), Address);
|
|
ASSERT(FALSE);
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
/*
|
|
* Find the memory area for the faulting address
|
|
*/
|
|
if (Address >= (ULONG_PTR)MmSystemRangeStart)
|
|
{
|
|
/*
|
|
* Check permissions
|
|
*/
|
|
if (Mode != KernelMode)
|
|
{
|
|
DPRINTC("Address: %x\n", Address);
|
|
return(STATUS_ACCESS_VIOLATION);
|
|
}
|
|
AddressSpace = MmGetKernelAddressSpace();
|
|
}
|
|
else
|
|
{
|
|
AddressSpace = &PsGetCurrentProcess()->Vm;
|
|
}
|
|
|
|
Thread->ActiveFaultCount++;
|
|
Status = MmNotPresentFaultCacheSectionInner
|
|
(Mode, AddressSpace, Address, FromMdl, Thread);
|
|
Thread->ActiveFaultCount--;
|
|
|
|
ASSERT(Status != STATUS_UNSUCCESSFUL);
|
|
ASSERT(Status != STATUS_INVALID_PARAMETER);
|
|
DPRINT("MmAccessFault %x:%x -> %x\n", MmGetAddressSpaceOwner(AddressSpace), Address, Status);
|
|
|
|
return(Status);
|
|
}
|