From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/ntos/mm/umapview.c | 660 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 660 insertions(+) create mode 100644 private/ntos/mm/umapview.c (limited to 'private/ntos/mm/umapview.c') diff --git a/private/ntos/mm/umapview.c b/private/ntos/mm/umapview.c new file mode 100644 index 000000000..4798121a2 --- /dev/null +++ b/private/ntos/mm/umapview.c @@ -0,0 +1,660 @@ +/*++ + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + Umapview.c + +Abstract: + + This module contains the routines which implement the + NtUnmapViewOfSection service. + +Author: + + Lou Perazzoli (loup) 22-May-1989 + +Revision History: + +--*/ + +#include "mi.h" + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE,NtUnmapViewOfSection) +#pragma alloc_text(PAGE,MmUnmapViewOfSection) +#endif + + +NTSTATUS +NtUnmapViewOfSection( + IN HANDLE ProcessHandle, + IN PVOID BaseAddress + ) + +/*++ + +Routine Description: + + This function unmaps a previously created view to a section. + +Arguments: + + ProcessHandle - Supplies an open handle to a process object. + + BaseAddress - Supplies the base address of the view. + +Return Value: + + Returns the status + + TBS + + +--*/ + +{ + PEPROCESS Process; + KPROCESSOR_MODE PreviousMode; + NTSTATUS Status; + + PAGED_CODE(); + + PreviousMode = KeGetPreviousMode(); + + if ((PreviousMode == UserMode) && (BaseAddress > MM_HIGHEST_USER_ADDRESS)) { + return STATUS_NOT_MAPPED_VIEW; + } + + Status = ObReferenceObjectByHandle ( ProcessHandle, + PROCESS_VM_OPERATION, + PsProcessType, + PreviousMode, + (PVOID *)&Process, + NULL ); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + Status = MmUnmapViewOfSection ( Process, BaseAddress ); + ObDereferenceObject (Process); + + return Status; +} + +NTSTATUS +MmUnmapViewOfSection( + IN PEPROCESS Process, + IN PVOID BaseAddress + ) + +/*++ + +Routine Description: + + This function unmaps a previously created view to a section. + +Arguments: + + Process - Supplies a referenced pointer to a process object. + + BaseAddress - Supplies the base address of the view. + +Return Value: + + Returns the status + + TBS + + +--*/ + +{ + PMMVAD Vad; + PMMVAD PreviousVad; + PMMVAD NextVad; + ULONG RegionSize; + PVOID UnMapImageBase; + NTSTATUS status; + + PAGED_CODE(); + + UnMapImageBase = NULL; + + // + // If the specified process is not the current process, attach + // to the specified process. + // + + + KeAttachProcess (&Process->Pcb); + + // + // Get the address creation mutex to block multiple threads from + // creating or deleting address space at the same time and + // get the working set mutex so virtual address descriptors can + // be inserted and walked. + // Raise IRQL to block APCs. + // + // Get the working set mutex, no page faults allowed for now until + // working set mutex released. + // + + + LOCK_WS_AND_ADDRESS_SPACE (Process); + + // + // Make sure the address space was not deleted, if so, return an error. + // + + if (Process->AddressSpaceDeleted != 0) { + status = STATUS_PROCESS_IS_TERMINATING; + goto ErrorReturn; + } + + // + // Find the associated vad. + // + + Vad = MiLocateAddress (BaseAddress); + + if ((Vad == (PMMVAD)NULL) || (Vad->u.VadFlags.PrivateMemory)) { + + // + // No Virtual Address Descriptor located for Base Address. + // + + status = STATUS_NOT_MAPPED_VIEW; + goto ErrorReturn; + } + + if (Vad->u.VadFlags.NoChange == 1) { + + // + // An attempt is being made to delete a secured VAD, check + // to see if this deletion is allowed. + // + + status = MiCheckSecuredVad ((PMMVAD)Vad, + Vad->StartingVa, + 1, + MM_SECURE_DELETE_CHECK); + + if (!NT_SUCCESS (status)) { + goto ErrorReturn; + } + } + + // + // If this Vad is for an image section, then + // get the base address of the section + // + + if ((Vad->u.VadFlags.ImageMap == 1) && (Process == PsGetCurrentProcess())) { + UnMapImageBase = Vad->StartingVa; + } + + RegionSize = 1 + (ULONG)Vad->EndingVa - (ULONG)Vad->StartingVa; + + PreviousVad = MiGetPreviousVad (Vad); + NextVad = MiGetNextVad (Vad); + + MiRemoveVad (Vad); + + // + // Return commitment for page table pages if possibible. + // + + MiReturnPageTablePageCommitment (Vad->StartingVa, + Vad->EndingVa, + Process, + PreviousVad, + NextVad); + + MiRemoveMappedView (Process, Vad); + + ExFreePool (Vad); + + // + // Update the current virtual size in the process header. + // + + Process->VirtualSize -= RegionSize; + status = STATUS_SUCCESS; + +ErrorReturn: + + UNLOCK_WS (Process); + UNLOCK_ADDRESS_SPACE (Process); + + if ( UnMapImageBase ) { + DbgkUnMapViewOfSection(UnMapImageBase); + } + KeDetachProcess(); + + return status; +} + +VOID +MiRemoveMappedView ( + IN PEPROCESS CurrentProcess, + IN PMMVAD Vad + ) + +/*++ + +Routine Description: + + This function removes the mapping from the current process's + address space. + +Arguments: + + Process - Supplies a referenced pointer to the currnt process object. + + Vad - Supplies the VAD which maps the view. + +Return Value: + + None. + +Environment: + + APC level, working set mutex and address creation mutex held. + + NOTE: THE WORKING SET MUTEXS MAY BE RELEASED THEN REACQUIRED!!!! + +--*/ + +{ + KIRQL OldIrql; + BOOLEAN DereferenceSegment = FALSE; + PCONTROL_AREA ControlArea; + PMMPTE PointerPte; + PMMPTE PointerPde; + PMMPTE LastPte; + ULONG PageTableOffset; + ULONG PdePage; + PKEVENT PurgeEvent = NULL; + PVOID TempVa; + BOOLEAN DeleteOnClose = FALSE; + MMPTE_FLUSH_LIST PteFlushList; + + ControlArea = Vad->ControlArea; + + if (Vad->u.VadFlags.PhysicalMapping == 1) { + + if (Vad->Banked) { + ExFreePool (Vad->Banked); + } + +#ifdef LARGE_PAGES + if (Vad->u.VadFlags.LargePages == 1) { + + // + // Delete the subsection allocated to hold the large pages. + // + + ExFreePool (Vad->FirstPrototypePte); + Vad->FirstPrototypePte = NULL; + KeFlushEntireTb (TRUE, FALSE); + LOCK_PFN (OldIrql); + } else { + +#endif //LARGE_PAGES + + // + // This is a physical memory view. The pages map physical memory + // and are not accounted for in the working set list or in the PFN + // database. + // + + // + // Set count so only flush entire TB operations are performed. + // + + PteFlushList.Count = MM_MAXIMUM_FLUSH_COUNT; + + LOCK_PFN (OldIrql); + + // + // Remove the PTES from the address space. + // + + PointerPde = MiGetPdeAddress (Vad->StartingVa); + PdePage = PointerPde->u.Hard.PageFrameNumber; + PointerPte = MiGetPteAddress (Vad->StartingVa); + LastPte = MiGetPteAddress (Vad->EndingVa); + PageTableOffset = MiGetPteOffset( PointerPte ); + + while (PointerPte <= LastPte) { + + if (((ULONG)PointerPte & (PAGE_SIZE - 1)) == 0) { + + PointerPde = MiGetPteAddress (PointerPte); + PageTableOffset = MiGetPteOffset( PointerPte ); + PdePage = PointerPde->u.Hard.PageFrameNumber; + } + + *PointerPte = ZeroPte; + MiDecrementShareAndValidCount (PdePage); + + // + // Decrement the count of non-zero page table entires for this + // page table. + // + + MmWorkingSetList->UsedPageTableEntries[PageTableOffset] -= 1; + ASSERT (MmWorkingSetList->UsedPageTableEntries[PageTableOffset] + < PTE_PER_PAGE); + + // + // If all the entries have been eliminated from the previous + // page table page, delete the page table page itself. + // + + if (MmWorkingSetList->UsedPageTableEntries[PageTableOffset] == + 0) { + + TempVa = MiGetVirtualAddressMappedByPte(PointerPde); + + PteFlushList.Count = MM_MAXIMUM_FLUSH_COUNT; + + MiDeletePte (PointerPde, + TempVa, + FALSE, + CurrentProcess, + (PMMPTE)NULL, + &PteFlushList); + + } + PointerPte += 1; + } + KeFlushEntireTb (TRUE, FALSE); +#ifdef LARGE_PAGES + } +#endif //LARGE_PAGES + } else { + + LOCK_PFN (OldIrql); + MiDeleteVirtualAddresses (Vad->StartingVa, + Vad->EndingVa, + FALSE, + Vad); + } + + // + // Decrement the count of the number of views for the + // Segment object. This requires the PFN mutex to be held (it is already). + // + + ControlArea->NumberOfMappedViews -= 1; + ControlArea->NumberOfUserReferences -= 1; + + // + // Check to see if the control area (segment) should be deleted. + // This routine releases the PFN lock. + // + + MiCheckControlArea (ControlArea, CurrentProcess, OldIrql); + + return; +} + +VOID +MiPurgeImageSection ( + IN PCONTROL_AREA ControlArea, + IN PEPROCESS Process OPTIONAL + ) + +/*++ + +Routine Description: + + This function locates subsections within an image section that + contain global memory and resets the global memory back to + the initial subsection contents. + + Note, that for this routine to be called the section is not + referenced nor is it mapped in any process. + +Arguments: + + ControlArea - Supplies a pointer to the control area for the section. + + Process - Supplies a pointer to the process IFF the working set mutex + is held, else NULL is supplied. + +Return Value: + + None. + +Environment: + PFN LOCK held. + +--*/ + +{ + PMMPTE PointerPte; + PMMPTE LastPte; + PMMPFN Pfn1; + MMPTE PteContents; + MMPTE NewContents; + MMPTE NewContentsDemandZero; + KIRQL OldIrql = APC_LEVEL; + ULONG i; + ULONG SizeOfRawData; + ULONG OffsetIntoSubsection; + PSUBSECTION Subsection; +#if DBG + ULONG DelayCount = 0; +#endif //DBG + + + i = ControlArea->NumberOfSubsections; + Subsection = (PSUBSECTION)(ControlArea + 1); + + // + // Loop through all the subsections + + while (i > 0) { + + if (Subsection->u.SubsectionFlags.GlobalMemory == 1) { + + NewContents.u.Long = 0; + NewContentsDemandZero.u.Long = 0; + SizeOfRawData = 0; + OffsetIntoSubsection = 0; + + // + // Purge this section. + // + + if (Subsection->StartingSector != 0) { + + // + // This is a not a demand zero section. + // + + NewContents.u.Long = + (ULONG)MiGetSubsectionAddressForPte(Subsection); + NewContents.u.Soft.Prototype = 1; + SizeOfRawData = ((Subsection->EndingSector << MMSECTOR_SHIFT) | + Subsection->u.SubsectionFlags.SectorEndOffset) - + (Subsection->StartingSector << MMSECTOR_SHIFT); + } + + NewContents.u.Soft.Protection = + Subsection->u.SubsectionFlags.Protection; + NewContentsDemandZero.u.Soft.Protection = + NewContents.u.Soft.Protection; + + PointerPte = Subsection->SubsectionBase; + LastPte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; + ControlArea = Subsection->ControlArea; + + MiMakeSystemAddressValidPfnWs (PointerPte, Process); + + while (PointerPte < LastPte) { + + if (((ULONG)PointerPte & (PAGE_SIZE - 1)) == 0) { + + // + // We are on a page boundary, make sure this PTE is resident. + // + + MiMakeSystemAddressValidPfnWs (PointerPte, Process); + } + + PteContents = *PointerPte; + if (PteContents.u.Long == 0) { + + // + // No more valid PTEs to deal with. + // + + break; + } + + ASSERT (PteContents.u.Hard.Valid == 0); + + if ((PteContents.u.Soft.Prototype == 0) && + (PteContents.u.Soft.Transition == 1)) { + + // + // The prototype PTE is in transition format. + // + + Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber); + + // + // If the prototype PTE is no longer pointing to + // the original image page (not in protopte format), + // or has been modified, remove it from memory. + // + + if ((Pfn1->u3.e1.Modified == 1) || + (Pfn1->OriginalPte.u.Soft.Prototype == 0)) { + ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0); + + // + // This is a transition PTE which has been + // modified or is no longer in protopte format. + // + + if (Pfn1->u3.e2.ReferenceCount != 0) { + + // + // There must be an I/O in progress on this + // page. Wait for the I/O operation to complete. + // + + UNLOCK_PFN (OldIrql); + + KeDelayExecutionThread (KernelMode, FALSE, &MmShortTime); + + // + // Redo the loop. + // +#if DBG + if ((DelayCount % 1024) == 0) { + DbgPrint("MMFLUSHSEC: waiting for i/o to complete PFN %lx\n", + Pfn1); + } + DelayCount += 1; +#endif //DBG + + LOCK_PFN (OldIrql); + + MiMakeSystemAddressValidPfnWs (PointerPte, Process); + continue; + } + + ASSERT (!((Pfn1->OriginalPte.u.Soft.Prototype == 0) && + (Pfn1->OriginalPte.u.Soft.Transition == 1))); + + *PointerPte = Pfn1->OriginalPte; + ASSERT (Pfn1->OriginalPte.u.Hard.Valid == 0); + + // + // Only reduce the number of PFN references if + // the original PTE is still in prototype PTE + // format. + // + + if (Pfn1->OriginalPte.u.Soft.Prototype == 1) { + ControlArea->NumberOfPfnReferences -= 1; + ASSERT ((LONG)ControlArea->NumberOfPfnReferences >= 0); + } + MiUnlinkPageFromList (Pfn1); + + MI_SET_PFN_DELETED (Pfn1); + + MiDecrementShareCount (Pfn1->PteFrame); + + // + // If the reference count for the page is zero, insert + // it into the free page list, otherwize leave it alone + // and when the reference count is decremented to zero + // the page will go to the free list. + // + + if (Pfn1->u3.e2.ReferenceCount == 0) { + MiReleasePageFileSpace (Pfn1->OriginalPte); + MiInsertPageInList (MmPageLocationList[FreePageList], + PteContents.u.Trans.PageFrameNumber); + } + + *PointerPte = NewContents; + } + } else { + + // + // Prototype PTE is not in transition format. + // + + if (PteContents.u.Soft.Prototype == 0) { + + // + // This refers to a page in the paging file, + // as it no longer references the image, + // restore the PTE contents to what they were + // at the initial image creation. + // + + if (PteContents.u.Long != NoAccessPte.u.Long) { + MiReleasePageFileSpace (PteContents); + *PointerPte = NewContents; + } + } + } + PointerPte += 1; + OffsetIntoSubsection += PAGE_SIZE; + + if (OffsetIntoSubsection >= SizeOfRawData) { + + // + // There are trailing demand zero pages in this + // subsection, set the PTE contents to be demand + // zero for the remainder of the PTEs in this + // subsection. + // + + NewContents = NewContentsDemandZero; + } + +#if DBG + DelayCount = 0; +#endif //DBG + + } //end while + } + + i -=1; + Subsection += 1; + } + + return; +} -- cgit v1.2.3