/*
* 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(AddressSpace->Process, Address))
{
if (Locked)
{
MmLockPage(MmGetPfnForProcess(AddressSpace->Process, Address));
}
return(STATUS_SUCCESS);
}
Segment = MemoryArea->Data.SectionData.Segment;
Section = MemoryArea->Data.SectionData.Section;
Region = MmFindRegion(MemoryArea->StartingAddress,
&MemoryArea->Data.SectionData.RegionListHead,
Address, NULL);
/*
* Lock the segment
*/
MmLockSectionSegment(Segment);
/*
* Check if this page needs to be mapped COW
*/
if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
(Region->Protect == PAGE_READWRITE ||
Region->Protect == PAGE_EXECUTE_READWRITE))
{
Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
}
else
{
Attributes = Region->Protect;
}
/*
* Get or create a page operation descriptor
*/ PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset, MM_PAGEOP_PAGEIN, FALSE);// 假设这里获得的PageOp已经有一个线程在做PAGEOUT操作,根据帖子尾部的 MmGetPageOp假如有个线程在做页面操作(比如PAGEOUT)那么根据这个函数的最后1个参数是FALSE,那么这儿PageOp的引用计数加1,但是如果是页面换出操作(PAGEOUT)完成了,下面的MmspWaitForPageOpCompletionEvent(PageOp)等待的线程就会都向下运行,而PAGEOUT完成了,就会用帖子尾部的MmReleasePageOp释放PageOp(实际上就是减少引用计数,如果为0,则删除),如果有多个线程等待(多个线程获取了PageOp,引用计数累加)那么运行到下面的if (PageOp->OpType != MM_PAGEOP_PAGEIN)则会释放PageOp(减少引用计数)并return(STATUS_MM_RESTART_OPERATION);在帖子后面的MmNotPresentFault重新进入循环重新进行case MEMORY_AREA_SECTION_VIEW:
Status = MmNotPresentFaultSectionView(AddressSpace,
MemoryArea,
(PVOID)Address,
Locked);
break;重新进入MmNotPresentFaultSectionView,重新进入后又遇到MmGetPageOp又会获取PageOp(增加引用计数)这一增一减引用计数不是还没有变化吗,然后又在事件上等待,此时另一个线程运行到if (PageOp->OpType != MM_PAGEOP_PAGEIN),设置事件让等待的线程向下运行并释放PageOp(减少引用计数)然后又重新进入循环重新进入MmNotPresentFaultSectionView然后又获取PageOp(增加引用计数)这样的话不是PAGEOUT的pageOp永远没机会删除吗,这样永远没机会进行PAGE_IN操作了
if (PageOp == NULL)
{
DPRINT1("MmGetPageOp failed\n");
KEBUGCHECK(0);
}
/*
* Check if someone else is already handling this fault, if so wait
* for them
*/
if (PageOp->Thread != PsGetCurrentThread())
{
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace); Status = MmspWaitForPageOpCompletionEvent(PageOp);//看上面注释,有多个线程等待PAGEOUT完成 /*
* Check for various strange conditions
*/
if (Status != STATUS_SUCCESS)
{
DPRINT1("Failed to wait for page op, status = %x\n", Status);
KEBUGCHECK(0);
}
if (PageOp->Status == STATUS_PENDING)
{
DPRINT1("Woke for page op before completion\n");
KEBUGCHECK(0);
}
MmLockAddressSpace(AddressSpace);
/*
* If this wasn't a pagein then restart the operation
*/
if (PageOp->OpType != MM_PAGEOP_PAGEIN)//如果有正在处理的PageOP类型不是PAGEIIN
{ MmspCompleteAndReleasePageOp(PageOp);//则设置事件释放PAGEOP(减少引用计数)
DPRINT("Address 0x%.8X\n", Address);
return(STATUS_MM_RESTART_OPERATION);//返回上层MmNotPresentFault中重新循环运行到此: case MEMORY_AREA_SECTION_VIEW:
Status = MmNotPresentFaultSectionView(AddressSpace,
MemoryArea,
(PVOID)Address,
Locked);
break;
重新进入MmNotPresentFaultSectionView
}
/*
* If the thread handling this fault has failed then we don't retry
*/
if (!NT_SUCCESS(PageOp->Status))
{
Status = PageOp->Status;
MmspCompleteAndReleasePageOp(PageOp);
DPRINT("Address 0x%.8X\n", Address);
return(Status);
}
MmLockSectionSegment(Segment);
/*
* If the completed fault was for another address space then set the
* page in this one.
*/
if (!MmIsPagePresent(AddressSpace->Process, Address))
{
Entry = MmGetPageEntrySectionSegment(Segment, Offset);
HasSwapEntry = MmIsPageSwapEntry(AddressSpace->Process, (PVOID)PAddress);
if (PAGE_FROM_SSE(Entry) == 0 || HasSwapEntry)
{
/*
* The page was a private page in another or in our address space
*/
MmUnlockSectionSegment(Segment);
MmspCompleteAndReleasePageOp(PageOp);
return(STATUS_MM_RESTART_OPERATION);
}
Page = PFN_FROM_SSE(Entry);
MmSharePageEntrySectionSegment(Segment, Offset);
/* FIXME: Should we call MmCreateVirtualMappingUnsafe if
* (Section->AllocationAttributes & SEC_PHYSICALMEMORY) is true?
*/
Status = MmCreateVirtualMapping(AddressSpace->Process,
Address,
Attributes,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to create virtual mapping\n");
KEBUGCHECK(0);
}
MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
}
if (Locked)
{
MmLockPage(Page);
}
MmUnlockSectionSegment(Segment);
PageOp->Status = STATUS_SUCCESS;
MmspCompleteAndReleasePageOp(PageOp);
DPRINT("Address 0x%.8X\n", Address);
return(STATUS_SUCCESS);
}
HasSwapEntry = MmIsPageSwapEntry(AddressSpace->Process, (PVOID)PAddress);
if (HasSwapEntry)
{
/*
* Must be private page we have swapped out.
*/
SWAPENTRY SwapEntry;
/*
* Sanity check
*/
if (Segment->Flags & MM_PAGEFILE_SEGMENT)
{
DPRINT1("Found a swaped out private page in a pagefile section.\n");
KEBUGCHECK(0);
}
MmUnlockAddressSpace(AddressSpace);
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
if (!NT_SUCCESS(Status))
{
KEBUGCHECK(0);
}
Status = MmReadFromSwapPage(SwapEntry, Page);
if (!NT_SUCCESS(Status))
{
DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
KEBUGCHECK(0);
}
MmLockAddressSpace(AddressSpace);
Status = MmCreateVirtualMapping(AddressSpace->Process,
Address,
Region->Protect,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
KEBUGCHECK(0);
return(Status);
}
/*
* Store the swap entry for later use.
*/
MmSetSavedSwapEntryPage(Page, SwapEntry);
/*
* Add the page to the process's working set
*/
MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
/*
* Finish the operation
*/
if (Locked)
{
MmLockPage(Page);
}
PageOp->Status = STATUS_SUCCESS;
MmspCompleteAndReleasePageOp(PageOp);
DPRINT("Address 0x%.8X\n", Address);
return(STATUS_SUCCESS);
}
/*
* Satisfying a page fault on a map of /Device/PhysicalMemory is easy
*/
if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
{
MmUnlockSectionSegment(Segment);
/*
* Just map the desired physical page
*/
Page = Offset >> PAGE_SHIFT;
Status = MmCreateVirtualMappingUnsafe(AddressSpace->Process,
Address,
Region->Protect,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
KEBUGCHECK(0);
return(Status);
}
/*
* Don't add an rmap entry since the page mapped could be for
* anything.
*/
if (Locked)
{
MmLockPageUnsafe(Page);
}
/*
* Get the entry corresponding to the offset within the section
*/
Entry = MmGetPageEntrySectionSegment(Segment, Offset);
if (Entry == 0)
{
/*
* If the entry is zero (and it can't change because we have
* locked the segment) then we need to load the page.
*/
/*
* Release all our locks and read in the page from disk
*/
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
(Offset >= PAGE_ROUND_UP(Segment->RawLength) && Section->AllocationAttributes & SEC_IMAGE))
{
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
if (!NT_SUCCESS(Status))
{
DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
}
}
else
{
Status = MiReadPage(MemoryArea, Offset, &Page);
if (!NT_SUCCESS(Status))
{
DPRINT1("MiReadPage failed (Status %x)\n", Status);
}
}
if (!NT_SUCCESS(Status))
{
/*
* FIXME: What do we know in this case?
*/
/*
* Cleanup and release locks
*/
MmLockAddressSpace(AddressSpace);
PageOp->Status = Status;
MmspCompleteAndReleasePageOp(PageOp);
DPRINT("Address 0x%.8X\n", Address);
return(Status);
}
/*
* Relock the address space and segment
*/
MmLockAddressSpace(AddressSpace);
MmLockSectionSegment(Segment);
/*
* Check the entry. No one should change the status of a page
* that has a pending page-in.
*/
Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
if (Entry != Entry1)
{
DPRINT1("Someone changed ppte entry while we slept\n");
KEBUGCHECK(0);
}
/*
* Mark the offset within the section as having valid, in-memory
* data
*/
Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
MmUnlockSectionSegment(Segment);
Status = MmCreateVirtualMapping(AddressSpace->Process,
Address,
Attributes,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to create virtual mapping\n");
KEBUGCHECK(0);
}
MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
if (Locked)
{
MmLockPage(Page);
}
PageOp->Status = STATUS_SUCCESS;
MmspCompleteAndReleasePageOp(PageOp);
DPRINT("Address 0x%.8X\n", Address);
return(STATUS_SUCCESS);
}
else if (IS_SWAP_FROM_SSE(Entry))
{
SWAPENTRY SwapEntry;
SwapEntry = SWAPENTRY_FROM_SSE(Entry);
/*
* Release all our locks and read in the page from disk
*/
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
if (!NT_SUCCESS(Status))
{
KEBUGCHECK(0);
}
Status = MmReadFromSwapPage(SwapEntry, Page);
if (!NT_SUCCESS(Status))
{
KEBUGCHECK(0);
}
/*
* Relock the address space and segment
*/
MmLockAddressSpace(AddressSpace);
MmLockSectionSegment(Segment);
/*
* Check the entry. No one should change the status of a page
* that has a pending page-in.
*/
Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
if (Entry != Entry1)
{
DPRINT1("Someone changed ppte entry while we slept\n");
KEBUGCHECK(0);
}
/*
* Mark the offset within the section as having valid, in-memory
* data
*/
Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
MmUnlockSectionSegment(Segment);
/*
* Save the swap entry.
*/
MmSetSavedSwapEntryPage(Page, SwapEntry);
Status = MmCreateVirtualMapping(AddressSpace->Process,
Address,
Region->Protect,
&Page,
1);
if (!NT_SUCCESS(Status))
{
DPRINT1("Unable to create virtual mapping\n");
KEBUGCHECK(0);
}
MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
if (Locked)
{
MmLockPage(Page);
}
PageOp->Status = STATUS_SUCCESS;
MmspCompleteAndReleasePageOp(PageOp);
DPRINT("Address 0x%.8X\n", Address);
return(STATUS_SUCCESS);
}
else
{
/*
* If the section offset is already in-memory and valid then just
* take another reference to the page
*/
if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
{
CPRINT("Page fault at high IRQL was %d, address %x\n", KeGetCurrentIrql(), Address);
return(STATUS_UNSUCCESSFUL);
}
if (PsGetCurrentProcess() == NULL)
{
/* Allow this! It lets us page alloc much earlier! It won't be needed
* after my init patch anyways
*/
DPRINT("No current process\n");
if (Address < (ULONG_PTR)MmSystemRangeStart)
{
return(STATUS_ACCESS_VIOLATION);
}
}
/*
* Find the memory area for the faulting address
*/
if (Address >= (ULONG_PTR)MmSystemRangeStart)
{
/*
* Check permissions
*/
if (Mode != KernelMode)
{
CPRINT("Address: %x\n", Address);
return(STATUS_ACCESS_VIOLATION);
}
AddressSpace = MmGetKernelAddressSpace();
}
else
{
AddressSpace = (PMADDRESS_SPACE)&(PsGetCurrentProcess())->VadRoot;
}
if (!FromMdl)
{
MmLockAddressSpace(AddressSpace);
}
/*
* Call the memory area specific fault handler
*/
do
{
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
{
if (!FromMdl)
{
MmUnlockAddressSpace(AddressSpace);
}
return (STATUS_ACCESS_VIOLATION);
}
switch (MemoryArea->Type)
{
case MEMORY_AREA_PAGED_POOL:
{
Status = MmCommitPagedPoolAddress((PVOID)Address, Locked);
break;
}
case MEMORY_AREA_SYSTEM:
Status = STATUS_ACCESS_VIOLATION;
break;
case MEMORY_AREA_SECTION_VIEW:
Status = MmNotPresentFaultSectionView(AddressSpace,
MemoryArea,
(PVOID)Address,
Locked);
break;
case MEMORY_AREA_VIRTUAL_MEMORY:
case MEMORY_AREA_PEB_OR_TEB:
Status = MmNotPresentFaultVirtualMemory(AddressSpace,
MemoryArea,
(PVOID)Address,
Locked);
break;
case MEMORY_AREA_SHARED_DATA:
Pfn = MmSharedDataPagePhysicalAddress.LowPart >> PAGE_SHIFT;
Status =
MmCreateVirtualMapping(PsGetCurrentProcess(),
(PVOID)PAGE_ROUND_DOWN(Address),
PAGE_READONLY,
&Pfn,
1);
break;
default:
Status = STATUS_ACCESS_VIOLATION;
break;
}
}
while (Status == STATUS_MM_RESTART_OPERATION);
PMM_PAGEOP
NTAPI MmGetPageOp(PMEMORY_AREA MArea, HANDLE Pid, PVOID Address,
PMM_SECTION_SEGMENT Segment, ULONG Offset, ULONG OpType, BOOLEAN First)
/*
* FUNCTION: Get a page operation descriptor corresponding to
* the memory area and either the segment, offset pair or the
* pid, address pair.
*/
{
ULONG_PTR Hash;
KIRQL oldIrql;
PMM_PAGEOP PageOp;
if (!PageOpReady)
{
DPRINT1("PAGEOPS USED TOO SOON!!!\n");
while (TRUE);
}
/*
* Calcuate the hash value for pageop structure
*/
if (MArea->Type == MEMORY_AREA_SECTION_VIEW)
{
Hash = (((ULONG_PTR)Segment) | (((ULONG_PTR)Offset) / PAGE_SIZE));
}
else
{
Hash = (((ULONG_PTR)Pid) | (((ULONG_PTR)Address) / PAGE_SIZE));
}
Hash = Hash % PAGEOP_HASH_TABLE_SIZE;
/*
* Check for an existing pageop structure
*/
PageOp = MmPageOpHashTable[Hash];
while (PageOp != NULL)
{
if (MArea->Type == MEMORY_AREA_SECTION_VIEW)
{
if (PageOp->Segment == Segment &&
PageOp->Offset == Offset)
{
break;
}
}
else
{
if (PageOp->Pid == Pid &&
PageOp->Address == Address)
{
break;
}
}
PageOp = PageOp->Next;
}
/*
* If we found an existing pageop then increment the reference count
* and return it.
*/
if (PageOp != NULL)
{
if (First)
{
PageOp = NULL;
}
else
{ PageOp->ReferenceCount++; }
KeReleaseSpinLock(&MmPageOpHashTableLock, oldIrql);
return(PageOp);
}
/*
* Otherwise add a new pageop.
*/
PageOp = ExAllocateFromNPagedLookasideList(&MmPageOpLookasideList);
if (PageOp == NULL)
{
KeReleaseSpinLock(&MmPageOpHashTableLock, oldIrql);
KEBUGCHECK(0);
return(NULL);
}