* Herve Poussineau
*/
+/*
+
+I've generally organized fault handling code in newmm as handlers that run
+under a single lock acquisition, check the state, and either take necessary
+action atomically, or place a wait entry and return a continuation to the
+caller. This lends itself to code that has a simple, structured form,
+doesn't make assumptions about lock taking and breaking, and provides an
+obvious, graphic seperation between code that may block and code that isn't
+allowed to. This file contains the non-blocking half.
+
+In order to request a blocking operation to happen outside locks, place a
+function pointer in the provided MM_REQUIRED_RESOURCES struct and return
+STATUS_MORE_PROCESSING_REQUIRED. The function indicated will receive the
+provided struct and take action outside of any mm related locks and at
+PASSIVE_LEVEL. The same fault handler will be called again after the
+blocking operation succeeds. In this way, the fault handler can accumulate
+state, but will freely work while competing with other threads.
+
+Fault handlers in this file should check for an MM_WAIT_ENTRY in a page
+table they're using and return STATUS_SUCCESS + 1 if it's found. In that
+case, the caller will wait on the wait entry event until the competing thread
+is finished, and recall this handler in the current thread.
+
+Another thing to note here is that we require mappings to exactly mirror
+rmaps, so each mapping should be immediately followed by an rmap addition.
+
+*/
+
/* INCLUDES *****************************************************************/
#include <ntoskrnl.h>
extern KEVENT MmWaitPageEvent;
extern PMMWSL MmWorkingSetList;
+/*
+
+Multiple stage handling of a not-present fault in a data section.
+
+Required->State is used to accumulate flags that indicate the next action
+the handler should take.
+
+State & 2 is currently used to indicate that the page acquired by a previous
+callout is a global page to the section and should be placed in the section
+page table.
+
+Note that the primitive tail recursion done here reaches the base case when
+the page is present.
+
+*/
+
NTSTATUS
NTAPI
-MmNotPresentFaultCachePage
-(PMMSUPPORT AddressSpace,
- MEMORY_AREA* MemoryArea,
- PVOID Address,
- BOOLEAN Locked,
- PMM_REQUIRED_RESOURCES Required)
+MmNotPresentFaultCachePage (
+ _In_ PMMSUPPORT AddressSpace,
+ _In_ MEMORY_AREA* MemoryArea,
+ _In_ PVOID Address,
+ _In_ BOOLEAN Locked,
+ _Inout_ PMM_REQUIRED_RESOURCES Required)
{
- NTSTATUS Status;
- PVOID PAddress;
- ULONG Consumer;
- PMM_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.SectionData.Segment;
-
- TotalOffset.QuadPart += MemoryArea->Data.SectionData.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
- */
- MmLockSectionSegment(Segment);
-
- /*
- * Get the entry corresponding to the offset within the section
- */
- Entry = MmGetPageEntrySectionSegment(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 = MmSetPageEntrySectionSegment
- (Segment, &TotalOffset, Entry = MAKE_PFN_SSE(Required->Page[0]));
- if (!NT_SUCCESS(Status))
- {
- MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
- }
- MmUnlockSectionSegment(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 (_MI_PAGING_LEVELS == 2)
- if (Address < MmSystemRangeStart)
+ NTSTATUS Status;
+ PVOID PAddress;
+ ULONG Consumer;
+ PMM_SECTION_SEGMENT Segment;
+ LARGE_INTEGER FileOffset, TotalOffset;
+ ULONG_PTR Entry;
+ ULONG Attributes;
+ PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
+ KIRQL OldIrql;
+
+ 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.SectionData.Segment;
+
+ TotalOffset.QuadPart += MemoryArea->Data.SectionData.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 */
+ MmLockSectionSegment(Segment);
+
+ /* Get the entry corresponding to the offset within the section */
+ Entry = MmGetPageEntrySectionSegment(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 = MmSetPageEntrySectionSegment(Segment,
+ &TotalOffset,
+ Entry = MAKE_PFN_SSE(Required->Page[0]));
+ if (!NT_SUCCESS(Status))
+ {
+ MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
+ }
+ MmUnlockSectionSegment(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
{
- Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
- ASSERT(Process->Vm.VmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
+ /* Drop the reference for our address space ... */
+ MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
}
-#endif
- if (NT_SUCCESS(Status))
- {
- MmInsertRmap(Required->Page[0], Process, Address);
- }
- else
- {
- // Drop the reference for our address space ...
- MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
- }
- MmUnlockSectionSegment(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);
- MmUnlockSectionSegment(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;
- MmSetPageEntrySectionSegment(Segment, &TotalOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
- MmUnlockSectionSegment(Segment);
- return STATUS_MORE_PROCESSING_REQUIRED;
- }
- ASSERT(FALSE);
- return STATUS_ACCESS_VIOLATION;
+ MmUnlockSectionSegment(Segment);
+ DPRINTC("XXX Set Event %x\n", Status);
+ MiSetPageEvent(Process, Address);
+ DPRINT("Status %x\n", Status);
+ return Status;
+ }
+ }
+ else if (MM_IS_WAIT_PTE(Entry))
+ {
+ // Whenever MM_WAIT_ENTRY is required as a swap entry, we need to
+ // ask the fault handler to wait until we should continue. Rathern
+ // than recopy this boilerplate code everywhere, we just ask them
+ // to wait.
+ MmUnlockSectionSegment(Segment);
+ return STATUS_SUCCESS + 1;
+ }
+ else if (Entry)
+ {
+ PFN_NUMBER Page = PFN_FROM_SSE(Entry);
+ DPRINT("Take reference to page %x #\n", Page);
+
+ if (MiGetPfnEntry(Page) == NULL)
+ {
+ DPRINT1("Found no PFN entry for page 0x%x in page entry 0x%x (segment: 0x%p, offset: %08x%08x)\n",
+ Page,
+ Entry,
+ Segment,
+ TotalOffset.HighPart,
+ TotalOffset.LowPart);
+ KeBugCheck(CACHE_MANAGER);
+ }
+
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ MmReferencePage(Page);
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ 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);
+ MmUnlockSectionSegment(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;
+
+ MmSetPageEntrySectionSegment(Segment,
+ &TotalOffset,
+ MAKE_SWAP_SSE(MM_WAIT_ENTRY));
+
+ MmUnlockSectionSegment(Segment);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ ASSERT(FALSE);
+ return STATUS_ACCESS_VIOLATION;
}
NTSTATUS
PEPROCESS Process;
KIRQL Irql, Irql2;
PVOID TempAddress, TempSource;
-
+
Process = PsGetCurrentProcess();
TempAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
if (TempAddress == NULL)
{
- return(STATUS_NO_MEMORY);
+ return STATUS_NO_MEMORY;
+ }
+ TempSource = MiMapPageInHyperSpace(Process, SrcPage, &Irql2);
+ if (!TempSource) {
+ MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
+ 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, TempSource, Irql2);
MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
- return(STATUS_SUCCESS);
+ return STATUS_SUCCESS;
}
+/*
+
+This function is deceptively named, in that it does the actual work of handling
+access faults on data sections. In the case of the code that's present here,
+we don't allow cow sections, but we do need this to unset the initial
+PAGE_READONLY condition of pages faulted into the cache so that we can add
+a dirty bit in the section page table on the first modification.
+
+In the ultimate form of this code, CoW is reenabled.
+
+*/
+
NTSTATUS
NTAPI
-MiCowCacheSectionPage
-(PMMSUPPORT AddressSpace,
- PMEMORY_AREA MemoryArea,
- PVOID Address,
- BOOLEAN Locked,
- PMM_REQUIRED_RESOURCES Required)
+MiCowCacheSectionPage (
+ _In_ PMMSUPPORT AddressSpace,
+ _In_ PMEMORY_AREA MemoryArea,
+ _In_ PVOID Address,
+ _In_ BOOLEAN Locked,
+ _Inout_ PMM_REQUIRED_RESOURCES Required)
{
- PMM_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.SectionData.Segment;
-
- /*
- * Lock the segment
- */
- MmLockSectionSegment(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.SectionData.ViewOffset.QuadPart;
-
- if (!Segment->WriteCopy /*&&
- !MemoryArea->Data.SectionData.WriteCopyView*/ ||
- Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
- {
+ PMM_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.SectionData.Segment;
+
+ /* Lock the segment */
+ MmLockSectionSegment(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.SectionData.ViewOffset.QuadPart;
+
+ if (!Segment->WriteCopy /*&&
+ !MemoryArea->Data.SectionData.WriteCopyView*/ ||
+ Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
+ {
#if 0
- if (Region->Protect == PAGE_READWRITE ||
- Region->Protect == PAGE_EXECUTE_READWRITE)
+ if (Region->Protect == PAGE_READWRITE ||
+ Region->Protect == PAGE_EXECUTE_READWRITE)
#endif
- {
- ULONG Entry;
- 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);
- }
- Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
- DPRINT("Entry %x\n", Entry);
- if (Entry &&
- !IS_SWAP_FROM_SSE(Entry) &&
- PFN_FROM_SSE(Entry) == MmGetPfnForProcess(Process, Address)) {
- MmSetPageEntrySectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
- }
- MmSetPageProtect(Process, PAddress, PAGE_READWRITE);
- MmSetDirtyPage(Process, PAddress);
- MmUnlockSectionSegment(Segment);
- DPRINT("Done\n");
- return STATUS_SUCCESS;
- }
+ {
+ ULONG_PTR Entry;
+ 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);
+ }
+ Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
+ DPRINT("Entry %x\n", Entry);
+ if (Entry &&
+ !IS_SWAP_FROM_SSE(Entry) &&
+ PFN_FROM_SSE(Entry) == MmGetPfnForProcess(Process, Address)) {
+
+ MmSetPageEntrySectionSegment(Segment,
+ &Offset,
+ DIRTY_SSE(Entry));
+ }
+ MmSetPageProtect(Process, PAddress, PAGE_READWRITE);
+ MmSetDirtyPage(Process, PAddress);
+ MmUnlockSectionSegment(Segment);
+ DPRINT("Done\n");
+ return STATUS_SUCCESS;
+ }
#if 0
- else
- {
- DPRINT("Not supposed to be writable\n");
- MmUnlockSectionSegment(Segment);
- return STATUS_ACCESS_VIOLATION;
- }
+ else
+ {
+ DPRINT("Not supposed to be writable\n");
+ MmUnlockSectionSegment(Segment);
+ return STATUS_ACCESS_VIOLATION;
+ }
#endif
- }
-
- if (!Required->Page[0])
- {
- SWAPENTRY SwapEntry;
- if (MmIsPageSwapEntry(Process, Address))
- {
- MmGetPageFileMapping(Process, Address, &SwapEntry);
- MmUnlockSectionSegment(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);
- MmUnlockSectionSegment(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);
- MmUnlockSectionSegment(Segment);
- return(Status);
- }
-
- MmInsertRmap(NewPage, Process, PAddress);
- MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
- MmUnlockSectionSegment(Segment);
-
- DPRINT("Address 0x%.8X\n", Address);
- return(STATUS_SUCCESS);
+ }
+
+ if (!Required->Page[0])
+ {
+ SWAPENTRY SwapEntry;
+ if (MmIsPageSwapEntry(Process, Address))
+ {
+ MmGetPageFileMapping(Process, Address, &SwapEntry);
+ MmUnlockSectionSegment(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
+ }
+ /* Call out to acquire a page to copy to. We'll be re-called when
+ * the page has been allocated. */
+ 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);
+ MmUnlockSectionSegment(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);
+ MmUnlockSectionSegment(Segment);
+ return Status;
+ }
+
+ MmInsertRmap(NewPage, Process, PAddress);
+ MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
+ MmUnlockSectionSegment(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_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;
+/*
+
+This is the work item used do blocking resource acquisition when a fault
+handler returns STATUS_MORE_PROCESSING_REQUIRED. It's used to allow resource
+acquisition to take place on a different stack, and outside of any locks used
+by fault handling, making recursive fault handling possible when required.
+
+*/
+
+_Function_class_(WORKER_THREAD_ROUTINE)
VOID
NTAPI
-MmpFaultWorker
-(PWORK_QUEUE_WITH_CONTEXT WorkItem)
+MmpFaultWorker(PVOID Parameter)
{
- 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);
+ PWORK_QUEUE_WITH_CONTEXT WorkItem = Parameter;
+
+ 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);
}
+/*
+
+This code seperates the action of fault handling into an upper and lower
+handler to allow the inner handler to optionally be called in work item
+if the stack is getting too deep. My experiments show that the third
+recursive page fault taken at PASSIVE_LEVEL must be shunted away to a
+worker thread. In the ultimate form of this code, the primary fault handler
+makes this decision by using a thread-local counter to detect a too-deep
+fault stack and call the inner fault handler in a worker thread if required.
+
+Note that faults are taken at passive level and have access to ordinary
+driver entry points such as those that read and write files, and filesystems
+should use paged structures whenever possible. This makes recursive faults
+both a perfectly normal occurrance, and a worthwhile case to handle.
+
+The code below will repeatedly call MiCowSectionPage as long as it returns
+either STATUS_SUCCESS + 1 or STATUS_MORE_PROCESSING_REQUIRED. In the more
+processing required case, we call out to a blocking resource acquisition
+function and then recall the faut handler with the shared state represented
+by the MM_REQUIRED_RESOURCES struct.
+
+In the other case, we wait on the wait entry event and recall the handler.
+Each time the wait entry event is signalled, one thread has removed an
+MM_WAIT_ENTRY from a page table.
+
+In the ultimate form of this code, there is a single system wide fault handler
+for each of access fault and not present and each memory area contains a
+function pointer that indicates the active fault handler. Since the mm code
+in reactos is currently fragmented, I didn't bring this change to trunk.
+
+*/
+
NTSTATUS
NTAPI
-MmpSectionAccessFaultInner
-(KPROCESSOR_MODE Mode,
- PMMSUPPORT AddressSpace,
- ULONG_PTR Address,
- BOOLEAN FromMdl,
- PETHREAD Thread)
+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 };
- WORK_QUEUE_WITH_CONTEXT Context;
-
- RtlZeroMemory(&Context, sizeof(WORK_QUEUE_WITH_CONTEXT));
-
- 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)
- {
+ MEMORY_AREA* MemoryArea;
+ NTSTATUS Status;
+ BOOLEAN Locked = FromMdl;
+ MM_REQUIRED_RESOURCES Resources = { 0 };
+ WORK_QUEUE_WITH_CONTEXT Context;
+
+ RtlZeroMemory(&Context, sizeof(WORK_QUEUE_WITH_CONTEXT));
+
+ 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);
- }
- 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)
- {
- 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);
+ }
+
+ 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)
+ {
+ 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,
+ 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;
}
+/*
+
+This is the outer fault handler mentioned in the description of
+MmpSectionAccsesFaultInner. It increments a fault depth count in the current
+thread.
+
+In the ultimate form of this code, the lower fault handler will optionally
+use the count to keep the kernel stack from overflowing.
+
+*/
+
NTSTATUS
NTAPI
-MmAccessFaultCacheSection
-(KPROCESSOR_MODE Mode,
- ULONG_PTR Address,
- BOOLEAN FromMdl)
+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);
+ 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;
}
+/*
+
+As above, this code seperates the active part of fault handling from a carrier
+that can use the thread's active fault count to determine whether a work item
+is required. Also as above, this function repeatedly calls the active not
+present fault handler until a clear success or failure is received, using a
+return of STATUS_MORE_PROCESSING_REQUIRED or STATUS_SUCCESS + 1.
+
+*/
+
NTSTATUS
NTAPI
-MmNotPresentFaultCacheSectionInner
-(KPROCESSOR_MODE Mode,
- PMMSUPPORT AddressSpace,
- ULONG_PTR Address,
- BOOLEAN FromMdl,
- PETHREAD Thread)
+MmNotPresentFaultCacheSectionInner(KPROCESSOR_MODE Mode,
+ PMMSUPPORT AddressSpace,
+ ULONG_PTR Address,
+ BOOLEAN FromMdl,
+ PETHREAD Thread)
{
- BOOLEAN Locked = FromMdl;
- PMEMORY_AREA MemoryArea;
- MM_REQUIRED_RESOURCES Resources = { 0 };
- WORK_QUEUE_WITH_CONTEXT Context;
- NTSTATUS Status = STATUS_SUCCESS;
+ BOOLEAN Locked = FromMdl;
+ PMEMORY_AREA MemoryArea;
+ MM_REQUIRED_RESOURCES Resources = { 0 };
+ WORK_QUEUE_WITH_CONTEXT Context;
+ NTSTATUS Status = STATUS_SUCCESS;
RtlZeroMemory(&Context, sizeof(WORK_QUEUE_WITH_CONTEXT));
- 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 > 2)
- {
- 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;
+ 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 > 2)
+ {
+ 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;
}
+/*
+
+Call the inner not present fault handler, keeping track of the fault count.
+In the ultimate form of this code, optionally use a worker thread the handle
+the fault in order to sidestep stack overflow in the multiple fault case.
+
+*/
+
NTSTATUS
NTAPI
-MmNotPresentFaultCacheSection
-(KPROCESSOR_MODE Mode,
- ULONG_PTR Address,
- BOOLEAN FromMdl)
+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);
+ 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;
}