NTSTATUS NTAPI _MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset, ULONG_PTR Entry, const char *file, int line) { ULONG_PTR PageIndex, OldEntry; PCACHE_SECTION_PAGE_TABLE PageTable; ASSERT(Segment->Locked); ASSERT(!IS_SWAP_FROM_SSE(Entry) || !IS_DIRTY_SSE(Entry)); if (Entry && !IS_SWAP_FROM_SSE(Entry)) MmGetRmapListHeadPage(PFN_FROM_SSE(Entry)); PageTable = MiSectionPageTableGetOrAllocate(&Segment->PageTable, Offset); if (!PageTable) return STATUS_NO_MEMORY; ASSERT(MiSectionPageTableGet(&Segment->PageTable, Offset)); PageTable->Segment = Segment; PageIndex = (ULONG_PTR)((Offset->QuadPart - PageTable->FileOffset.QuadPart) / PAGE_SIZE); OldEntry = PageTable->PageEntries[PageIndex]; DPRINT("MiSetPageEntrySectionSegment(%p,%08x%08x,%x=>%x)\n", Segment, Offset->u.HighPart, Offset->u.LowPart, OldEntry, Entry); if (PFN_FROM_SSE(Entry) == PFN_FROM_SSE(OldEntry)) { /* Nothing */ } else if (Entry && !IS_SWAP_FROM_SSE(Entry)) { ASSERT(!OldEntry || IS_SWAP_FROM_SSE(OldEntry)); MmSetSectionAssociation(PFN_FROM_SSE(Entry), Segment, Offset); } else if (OldEntry && !IS_SWAP_FROM_SSE(OldEntry)) { ASSERT(!Entry || IS_SWAP_FROM_SSE(Entry)); MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry)); } else if (IS_SWAP_FROM_SSE(Entry)) { ASSERT(!IS_SWAP_FROM_SSE(OldEntry) || SWAPENTRY_FROM_SSE(OldEntry) == MM_WAIT_ENTRY); if (OldEntry && SWAPENTRY_FROM_SSE(OldEntry) != MM_WAIT_ENTRY) MmDeleteSectionAssociation(PFN_FROM_SSE(OldEntry)); } else if (IS_SWAP_FROM_SSE(OldEntry)) { ASSERT(!IS_SWAP_FROM_SSE(Entry)); if (Entry) MmSetSectionAssociation(PFN_FROM_SSE(OldEntry), Segment, Offset); } else { /* We should not be replacing a page like this */ ASSERT(FALSE); } PageTable->PageEntries[PageIndex] = Entry; return STATUS_SUCCESS; }
NTSTATUS NTAPI MmpPageOutPhysicalAddress(PFN_NUMBER Page) { BOOLEAN ProcRef = FALSE, PageDirty; PFN_NUMBER SectionPage = 0; PMM_RMAP_ENTRY entry; PMM_SECTION_SEGMENT Segment = NULL; LARGE_INTEGER FileOffset; PMEMORY_AREA MemoryArea; PMMSUPPORT AddressSpace = NULL; BOOLEAN Dirty = FALSE; PVOID Address = NULL; PEPROCESS Process = NULL; NTSTATUS Status = STATUS_SUCCESS; MM_REQUIRED_RESOURCES Resources = { 0 }; DPRINTC("Page out %x (ref ct %x)\n", Page, MmGetReferenceCountPage(Page)); ExAcquireFastMutex(&MiGlobalPageOperation); if ((Segment = MmGetSectionAssociation(Page, &FileOffset))) { DPRINTC("Withdrawing page (%x) %p:%x\n", Page, Segment, FileOffset.LowPart); SectionPage = MmWithdrawSectionPage(Segment, &FileOffset, &Dirty); DPRINTC("SectionPage %x\n", SectionPage); if (SectionPage == MM_WAIT_ENTRY || SectionPage == 0) { DPRINT1("In progress page out %x\n", SectionPage); ExReleaseFastMutex(&MiGlobalPageOperation); return STATUS_UNSUCCESSFUL; } else { ASSERT(SectionPage == Page); } Resources.State = Dirty ? 1 : 0; } else { DPRINT("No segment association for %x\n", Page); } Dirty = MmIsDirtyPageRmap(Page); DPRINTC("Trying to unmap all instances of %x\n", Page); ExAcquireFastMutex(&RmapListLock); entry = MmGetRmapListHeadPage(Page); // Entry and Segment might be null here in the case that the page // is new and is in the process of being swapped in if (!entry && !Segment) { Status = STATUS_UNSUCCESSFUL; DPRINT1("Page %x is in transit\n", Page); ExReleaseFastMutex(&RmapListLock); goto bail; } while (entry != NULL && NT_SUCCESS(Status)) { Process = entry->Process; Address = entry->Address; DPRINTC("Process %p Address %p Page %x\n", Process, Address, Page); if (RMAP_IS_SEGMENT(Address)) { entry = entry->Next; continue; } if (Process && Address < MmSystemRangeStart) { /* Make sure we don't try to page out part of an exiting process */ if (PspIsProcessExiting(Process)) { DPRINT("bail\n"); ExReleaseFastMutex(&RmapListLock); goto bail; } ObReferenceObject(Process); ProcRef = TRUE; AddressSpace = &Process->Vm; } else { AddressSpace = MmGetKernelAddressSpace(); } ExReleaseFastMutex(&RmapListLock); RtlZeroMemory(&Resources, sizeof(Resources)); if ((((ULONG_PTR)Address) & 0xFFF) != 0) { KeBugCheck(MEMORY_MANAGEMENT); } do { MmLockAddressSpace(AddressSpace); MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address); if (MemoryArea == NULL || MemoryArea->DeleteInProgress) { Status = STATUS_UNSUCCESSFUL; MmUnlockAddressSpace(AddressSpace); DPRINTC("bail\n"); goto bail; } DPRINTC("Type %x (%p -> %p)\n", MemoryArea->Type, MemoryArea->StartingAddress, MemoryArea->EndingAddress); Resources.DoAcquisition = NULL; Resources.Page[0] = Page; ASSERT(KeGetCurrentIrql() <= APC_LEVEL); DPRINT("%p:%p, page %x %x\n", Process, Address, Page, Resources.Page[0]); PageDirty = FALSE; Status = MmPageOutCacheSection(AddressSpace, MemoryArea, Address, &PageDirty, &Resources); Dirty |= PageDirty; DPRINT("%x\n", Status); ASSERT(KeGetCurrentIrql() <= APC_LEVEL); MmUnlockAddressSpace(AddressSpace); if (Status == STATUS_SUCCESS + 1) { // Wait page ... the other guy has it, so we'll just fail for now DPRINT1("Wait entry ... can't continue\n"); Status = STATUS_UNSUCCESSFUL; goto bail; } else if (Status == STATUS_MORE_PROCESSING_REQUIRED) { DPRINTC("DoAcquisition %p\n", Resources.DoAcquisition); Status = Resources.DoAcquisition(AddressSpace, MemoryArea, &Resources); DPRINTC("Status %x\n", Status); if (!NT_SUCCESS(Status)) { DPRINT1("bail\n"); goto bail; } else { Status = STATUS_MM_RESTART_OPERATION; } } } while (Status == STATUS_MM_RESTART_OPERATION); if (ProcRef) { ObDereferenceObject(Process); ProcRef = FALSE; } ExAcquireFastMutex(&RmapListLock); ASSERT(!MM_IS_WAIT_PTE(MmGetPfnForProcess(Process, Address))); entry = MmGetRmapListHeadPage(Page); DPRINTC("Entry %p\n", entry); } ExReleaseFastMutex(&RmapListLock); bail: DPRINTC("BAIL %x\n", Status); if (Segment) { ULONG RefCount; DPRINTC("About to finalize section page %x (%p:%x) Status %x %s\n", Page, Segment, FileOffset.LowPart, Status, Dirty ? "dirty" : "clean"); if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status = MmFinalizeSectionPageOut(Segment, &FileOffset, Page, Dirty))) { DPRINTC("Failed to page out %x, replacing %x at %x in segment %x\n", SectionPage, FileOffset.LowPart, Segment); MmLockSectionSegment(Segment); MmSetPageEntrySectionSegment(Segment, &FileOffset, Dirty ? MAKE_PFN_SSE(Page) : DIRTY_SSE(MAKE_PFN_SSE(Page))); MmUnlockSectionSegment(Segment); } /* Alas, we had the last reference */ if ((RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0) MmFinalizeSegment(Segment); } if (ProcRef) { DPRINTC("Dereferencing process...\n"); ObDereferenceObject(Process); } ExReleaseFastMutex(&MiGlobalPageOperation); DPRINTC("%s %x %x\n", NT_SUCCESS(Status) ? "Evicted" : "Spared", Page, Status); return NT_SUCCESS(Status) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; }