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; }
ULONG NTAPI MiCacheEvictPages(PMM_SECTION_SEGMENT Segment, ULONG Target) { ULONG_PTR Entry; ULONG Result = 0, i, j; NTSTATUS Status; PFN_NUMBER Page; LARGE_INTEGER Offset; MmLockSectionSegment(Segment); for (i = 0; i < RtlNumberGenericTableElements(&Segment->PageTable); i++) { PCACHE_SECTION_PAGE_TABLE Element = RtlGetElementGenericTable(&Segment->PageTable, i); ASSERT(Element); Offset = Element->FileOffset; for (j = 0; j < ENTRIES_PER_ELEMENT; j++, Offset.QuadPart += PAGE_SIZE) { Entry = MmGetPageEntrySectionSegment(Segment, &Offset); if (Entry && !IS_SWAP_FROM_SSE(Entry)) { Page = PFN_FROM_SSE(Entry); MmUnlockSectionSegment(Segment); Status = MmpPageOutPhysicalAddress(Page); if (NT_SUCCESS(Status)) Result++; MmLockSectionSegment(Segment); } } } MmUnlockSectionSegment(Segment); return Result; }
VOID NTAPI MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment, FREE_SECTION_PAGE_FUN FreePage) { PCACHE_SECTION_PAGE_TABLE Element; DPRINT("MiFreePageTablesSectionSegment(%p)\n", &Segment->PageTable); while ((Element = RtlGetElementGenericTable(&Segment->PageTable, 0))) { DPRINT("Delete table for <%wZ> %p -> %I64x\n", Segment->FileObject ? &Segment->FileObject->FileName : NULL, Segment, Element->FileOffset.QuadPart); if (FreePage) { ULONG i; for (i = 0; i < ENTRIES_PER_ELEMENT; i++) { ULONG_PTR Entry; LARGE_INTEGER Offset; Offset.QuadPart = Element->FileOffset.QuadPart + i * PAGE_SIZE; Entry = Element->PageEntries[i]; if (Entry && !IS_SWAP_FROM_SSE(Entry)) { DPRINT("Freeing page %p:%Ix @ %I64x\n", Segment, Entry, Offset.QuadPart); FreePage(Segment, &Offset); } } } DPRINT("Remove memory\n"); RtlDeleteElementGenericTable(&Segment->PageTable, Element); } DPRINT("Done\n"); }
NTSTATUS NTAPI 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(%p, %p, %p, %u)\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) #endif { ULONG_PTR Entry; DPRINTC("setting non-cow page %p %p:%p offset %I64x (%Ix) to writable\n", Segment, Process, PAddress, Offset.QuadPart, 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; } #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 } /* 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%p\n", Address); return STATUS_SUCCESS; }
/* MmWithdrawSectionPage removes a page entry from the section segment, replacing it with a wait entry. The caller must replace the wait entry with a 0, when any required writing is done. The wait entry must remain until the page is written to protect against cases where a fault brings a stale copy of the page back before writing is complete. */ PFN_NUMBER NTAPI MmWithdrawSectionPage(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER FileOffset, BOOLEAN *Dirty) { ULONG_PTR Entry; DPRINT("MmWithdrawSectionPage(%p,%08x%08x,%p)\n", Segment, FileOffset->HighPart, FileOffset->LowPart, Dirty); MmLockSectionSegment(Segment); Entry = MmGetPageEntrySectionSegment(Segment, FileOffset); *Dirty = !!IS_DIRTY_SSE(Entry); DPRINT("Withdraw %x (%x) of %wZ\n", FileOffset->LowPart, Entry, Segment->FileObject ? &Segment->FileObject->FileName : NULL); if (!Entry) { DPRINT("Stoeled!\n"); MmUnlockSectionSegment(Segment); return 0; } else if (MM_IS_WAIT_PTE(Entry)) { DPRINT("WAIT\n"); MmUnlockSectionSegment(Segment); return MM_WAIT_ENTRY; } else if (Entry && !IS_SWAP_FROM_SSE(Entry)) { DPRINT("Page %x\n", PFN_FROM_SSE(Entry)); *Dirty |= (Entry & 2); MmSetPageEntrySectionSegment(Segment, FileOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY)); MmUnlockSectionSegment(Segment); return PFN_FROM_SSE(Entry); } else { DPRINT1("SWAP ENTRY?! (%p:%08x%08x)\n", Segment, FileOffset->HighPart, FileOffset->LowPart); ASSERT(FALSE); MmUnlockSectionSegment(Segment); return 0; } }