From 3e8034d4a5a056ebe6149e1d31b76817830a560b Mon Sep 17 00:00:00 2001 From: Sir Richard Date: Tue, 19 Oct 2010 17:07:11 +0000 Subject: [PATCH] [NTOS]: Add support for unmapping ARM3 sections, destroying segments and control areas, and clearing out subsection PTEs. [NTOS]: Add support to MiDeletePte/MiDeleteVirtualAddresses to handle Section VADs. [NTOS]: Add support to MiDeletePte to handle valid, prototype PTEs. [NTOS]: Add MEM_TOP_DOWN support to ARM3 section code. [NTOS]: Add support for unmapping currently mapped ARM3 section views at process termination. [NTOS]: Use the new ARM3 section code for mapping the NLS section in the system (tests the system-view mapping code) and in each new process (tests the data-mapping code). Section is correctly unmapped at process termination time! svn path=/trunk/; revision=49206 --- reactos/ntoskrnl/ex/init.c | 6 +- reactos/ntoskrnl/mm/ARM3/miarm.h | 37 +++++ reactos/ntoskrnl/mm/ARM3/procsup.c | 26 ++-- reactos/ntoskrnl/mm/ARM3/section.c | 219 +++++++++++++++++++++++++++-- reactos/ntoskrnl/mm/ARM3/virtual.c | 217 +++++++++++++++++++++++----- 5 files changed, 444 insertions(+), 61 deletions(-) diff --git a/reactos/ntoskrnl/ex/init.c b/reactos/ntoskrnl/ex/init.c index 5b448efbbf1..f43b1964c67 100644 --- a/reactos/ntoskrnl/ex/init.c +++ b/reactos/ntoskrnl/ex/init.c @@ -296,7 +296,7 @@ ExpInitNls(IN PLOADER_PARAMETER_BLOCK LoaderBlock) NULL, &SectionSize, PAGE_READWRITE, - SEC_COMMIT, + SEC_COMMIT | 0x1, NULL); if (!NT_SUCCESS(Status)) { @@ -319,7 +319,7 @@ ExpInitNls(IN PLOADER_PARAMETER_BLOCK LoaderBlock) } /* Map the NLS Section in system space */ - Status = MmMapViewInSystemSpace(ExpNlsSectionPointer, + Status = MmMapViewInSystemSpace((PVOID)((ULONG_PTR)ExpNlsSectionPointer | 0x1), &SectionBase, &ExpNlsTableSize); if (!NT_SUCCESS(Status)) @@ -349,7 +349,7 @@ ExpInitNls(IN PLOADER_PARAMETER_BLOCK LoaderBlock) SectionBase = NULL; /* Map the section in the system process */ - Status = MmMapViewOfSection(ExpNlsSectionPointer, + Status = MmMapViewOfSection((PVOID)((ULONG_PTR)ExpNlsSectionPointer | 0x1), PsGetCurrentProcess(), &SectionBase, 0L, diff --git a/reactos/ntoskrnl/mm/ARM3/miarm.h b/reactos/ntoskrnl/mm/ARM3/miarm.h index 165d9ef3b7a..09f2ae1ab15 100644 --- a/reactos/ntoskrnl/mm/ARM3/miarm.h +++ b/reactos/ntoskrnl/mm/ARM3/miarm.h @@ -832,6 +832,22 @@ MiUnlockWorkingSet(IN PETHREAD Thread, KeLeaveGuardedRegion(); } +// +// Returns the ProtoPTE inside a VAD for the given VPN +// +FORCEINLINE +PMMPTE +MI_GET_PROTOTYPE_PTE_FOR_VPN(IN PMMVAD Vad, + IN ULONG_PTR Vpn) +{ + PMMPTE ProtoPte; + + /* Find the offset within the VAD's prototype PTEs */ + ProtoPte = Vad->FirstPrototypePte + ((Vpn - Vad->StartingVpn) * sizeof(MMPTE)); + ASSERT(ProtoPte <= Vad->LastContiguousPte); + return ProtoPte; +} + BOOLEAN NTAPI MmArmInitSystem( @@ -1224,6 +1240,27 @@ MiMakeSystemAddressValid( IN PVOID PageTableVirtualAddress, IN PEPROCESS CurrentProcess ); + +ULONG +NTAPI +MiMakeSystemAddressValidPfn( + IN PVOID VirtualAddress, + IN KIRQL OldIrql +); + +VOID +NTAPI +MiRemoveMappedView( + IN PEPROCESS CurrentProcess, + IN PMMVAD Vad +); + +PSUBSECTION +NTAPI +MiLocateSubsection( + IN PMMVAD Vad, + IN ULONG_PTR Vpn +); // // MiRemoveZeroPage will use inline code to zero out the page manually if only diff --git a/reactos/ntoskrnl/mm/ARM3/procsup.c b/reactos/ntoskrnl/mm/ARM3/procsup.c index 3afd8c4e9a7..0ad50bb738b 100644 --- a/reactos/ntoskrnl/mm/ARM3/procsup.c +++ b/reactos/ntoskrnl/mm/ARM3/procsup.c @@ -581,7 +581,7 @@ MmCreatePeb(IN PEPROCESS Process, // // Map NLS Tables // - Status = MmMapViewOfSection(ExpNlsSectionPointer, + Status = MmMapViewOfSection((PVOID)((ULONG_PTR)ExpNlsSectionPointer | 0x1), (PEPROCESS)Process, &TableBase, 0, @@ -1186,17 +1186,25 @@ MmCleanProcessAddressSpace(IN PEPROCESS Process) ASSERT(VadTree->NumberGenericTableElements >= 1); MiRemoveNode((PMMADDRESS_NODE)Vad, VadTree); - /* Only PEB/TEB VADs supported for now */ - ASSERT(Vad->u.VadFlags.PrivateMemory == 1); + /* Only regular VADs supported for now */ ASSERT(Vad->u.VadFlags.VadType == VadNone); - /* Delete the addresses */ - MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT, - (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1), - Vad); + /* Check if this is a section VAD */ + if (!(Vad->u.VadFlags.PrivateMemory) && (Vad->ControlArea)) + { + /* Remove the view */ + MiRemoveMappedView(Process, Vad); + } + else + { + /* Delete the addresses */ + MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT, + (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1), + Vad); - /* Release the working set */ - MiUnlockProcessWorkingSet(Process, Thread); + /* Release the working set */ + MiUnlockProcessWorkingSet(Process, Thread); + } /* Skip ARM3 fake VADs, they'll be freed by MmDeleteProcessAddresSpace */ if (Vad->u.VadFlags.Spare == 1) diff --git a/reactos/ntoskrnl/mm/ARM3/section.c b/reactos/ntoskrnl/mm/ARM3/section.c index 822c5c524d1..30f6b4c0a03 100644 --- a/reactos/ntoskrnl/mm/ARM3/section.c +++ b/reactos/ntoskrnl/mm/ARM3/section.c @@ -393,6 +393,188 @@ MiCheckPurgeAndUpMapCount(IN PCONTROL_AREA ControlArea, return STATUS_SUCCESS; } +PSUBSECTION +NTAPI +MiLocateSubsection(IN PMMVAD Vad, + IN ULONG_PTR Vpn) +{ + PSUBSECTION Subsection; + PCONTROL_AREA ControlArea; + ULONG PteOffset; + + /* Get the control area */ + ControlArea = Vad->ControlArea; + ASSERT(ControlArea->u.Flags.Rom == 0); + ASSERT(ControlArea->u.Flags.Image == 0); + ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); + + /* Get the subsection */ + Subsection = (PSUBSECTION)(ControlArea + 1); + + /* We only support single-subsection segments */ + ASSERT(Subsection->SubsectionBase != NULL); + ASSERT(Vad->FirstPrototypePte >= Subsection->SubsectionBase); + ASSERT(Vad->FirstPrototypePte < &Subsection->SubsectionBase[Subsection->PtesInSubsection]); + + /* Compute the PTE offset */ + PteOffset = (ULONG_PTR)Vpn - Vad->StartingVpn; + PteOffset += Vad->FirstPrototypePte - Subsection->SubsectionBase; + + /* Again, we only support single-subsection segments */ + ASSERT(PteOffset < 0xF0000000); + ASSERT(PteOffset < Subsection->PtesInSubsection); + + /* Return the subsection */ + return Subsection; +} + +VOID +NTAPI +MiSegmentDelete(IN PSEGMENT Segment) +{ + PCONTROL_AREA ControlArea; + SEGMENT_FLAGS SegmentFlags; + PSUBSECTION Subsection; + PMMPTE PointerPte, LastPte, PteForProto; + MMPTE TempPte; + KIRQL OldIrql; + + /* Capture data */ + SegmentFlags = Segment->SegmentFlags; + ControlArea = Segment->ControlArea; + + /* Make sure control area is on the right delete path */ + ASSERT(ControlArea->u.Flags.BeingDeleted == 1); + ASSERT(ControlArea->WritableUserReferences == 0); + + /* These things are not supported yet */ + ASSERT(ControlArea->DereferenceList.Flink == NULL); + ASSERT(!(ControlArea->u.Flags.Image) & !(ControlArea->u.Flags.File)); + ASSERT(ControlArea->u.Flags.GlobalOnlyPerSession == 0); + ASSERT(ControlArea->u.Flags.Rom == 0); + + /* Get the subsection and PTEs for this segment */ + Subsection = (PSUBSECTION)(ControlArea + 1); + PointerPte = Subsection->SubsectionBase; + LastPte = PointerPte + Segment->NonExtendedPtes; + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Check if the master PTE is invalid */ + PteForProto = MiAddressToPte(PointerPte); + if (!PteForProto->u.Hard.Valid) + { + /* Fault it in */ + MiMakeSystemAddressValidPfn(PointerPte, OldIrql); + } + + /* Loop all the segment PTEs */ + while (PointerPte < LastPte) + { + /* Check if it's time to switch master PTEs if we passed a PDE boundary */ + if (!((ULONG_PTR)PointerPte & (PD_SIZE - 1)) && + (PointerPte != Subsection->SubsectionBase)) + { + /* Check if the master PTE is invalid */ + PteForProto = MiAddressToPte(PointerPte); + if (!PteForProto->u.Hard.Valid) + { + /* Fault it in */ + MiMakeSystemAddressValidPfn(PointerPte, OldIrql); + } + } + + /* This should be a prototype PTE */ + TempPte = *PointerPte; + ASSERT(SegmentFlags.LargePages == 0); + ASSERT(TempPte.u.Hard.Valid == 0); + ASSERT(TempPte.u.Soft.Prototype == 1); + + /* Zero the PTE and keep going */ + PointerPte->u.Long = 0; + PointerPte++; + } + + /* Release the PFN lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Free the structures */ + ExFreePool(ControlArea); + ExFreePool(Segment); +} + +VOID +NTAPI +MiCheckControlArea(IN PCONTROL_AREA ControlArea, + IN KIRQL OldIrql) +{ + BOOLEAN DeleteSegment = FALSE; + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + /* Check if this is the last reference or view */ + if (!(ControlArea->NumberOfMappedViews) && + !(ControlArea->NumberOfSectionReferences)) + { + /* There should be no more user references either */ + ASSERT(ControlArea->NumberOfUserReferences == 0); + + /* Not yet supported */ + ASSERT(ControlArea->FilePointer == NULL); + + /* The control area is being destroyed */ + ControlArea->u.Flags.BeingDeleted = TRUE; + DeleteSegment = TRUE; + } + + /* Release the PFN lock */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Delete the segment if needed */ + if (DeleteSegment) + { + /* No more user write references at all */ + ASSERT(ControlArea->WritableUserReferences == 0); + MiSegmentDelete(ControlArea->Segment); + } +} + +VOID +NTAPI +MiRemoveMappedView(IN PEPROCESS CurrentProcess, + IN PMMVAD Vad) +{ + KIRQL OldIrql; + PCONTROL_AREA ControlArea; + + /* Get the control area */ + ControlArea = Vad->ControlArea; + + /* We only support non-extendable, non-image, pagefile-backed regular sections */ + ASSERT(Vad->u.VadFlags.VadType == VadNone); + ASSERT(Vad->u2.VadFlags2.ExtendableFile == FALSE); + ASSERT(ControlArea); + ASSERT(ControlArea->FilePointer == NULL); + + /* Delete the actual virtual memory pages */ + MiDeleteVirtualAddresses(Vad->StartingVpn << PAGE_SHIFT, + (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1), + Vad); + + /* Release the working set */ + MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread()); + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + + /* Remove references */ + ControlArea->NumberOfMappedViews--; + ControlArea->NumberOfUserReferences--; + + /* Check if it should be destroyed */ + MiCheckControlArea(ControlArea, OldIrql); +} + NTSTATUS NTAPI MiMapViewInSystemSpace(IN PVOID Section, @@ -505,7 +687,6 @@ MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea, /* These flags/parameters are not supported */ ASSERT((AllocationType & MEM_DOS_LIM) == 0); ASSERT((AllocationType & MEM_RESERVE) == 0); - ASSERT((AllocationType & MEM_TOP_DOWN) == 0); ASSERT(Process->VmTopDown == 0); ASSERT(Section->u.Flags.CopyOnWrite == FALSE); ASSERT(ZeroBits == 0); @@ -553,13 +734,28 @@ MiMapViewOfDataSection(IN PCONTROL_AREA ControlArea, /* Did the caller specify an address? */ if (!(*BaseAddress)) { - /* No, find an address bottom-up */ - Status = MiFindEmptyAddressRangeInTree(*ViewSize, - _64K, - &Process->VadRoot, - (PMMADDRESS_NODE*)&Process->VadFreeHint, - &StartAddress); - ASSERT(NT_SUCCESS(Status)); + /* Which way should we search? */ + if (AllocationType & MEM_TOP_DOWN) + { + /* No, find an address top-down */ + Status = MiFindEmptyAddressRangeDownTree(*ViewSize, + (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS, + _64K, + &Process->VadRoot, + &StartAddress, + (PMMADDRESS_NODE*)&Process->VadFreeHint); + ASSERT(NT_SUCCESS(Status)); + } + else + { + /* No, find an address bottom-up */ + Status = MiFindEmptyAddressRangeInTree(*ViewSize, + _64K, + &Process->VadRoot, + (PMMADDRESS_NODE*)&Process->VadFreeHint, + &StartAddress); + ASSERT(NT_SUCCESS(Status)); + } } else { @@ -1179,8 +1375,11 @@ NtCreateSection(OUT PHANDLE SectionHandle, SEC_LARGE_PAGES | SEC_IMAGE | SEC_NOCACHE | SEC_NO_CHANGE))) { - DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes); - return STATUS_INVALID_PARAMETER_6; + if (!(AllocationAttributes & 1)) + { + DPRINT1("Bogus allocation attribute: %lx\n", AllocationAttributes); + return STATUS_INVALID_PARAMETER_6; + } } /* Check for no allocation type */ diff --git a/reactos/ntoskrnl/mm/ARM3/virtual.c b/reactos/ntoskrnl/mm/ARM3/virtual.c index 785957b63f6..dd0f818e0ca 100644 --- a/reactos/ntoskrnl/mm/ARM3/virtual.c +++ b/reactos/ntoskrnl/mm/ARM3/virtual.c @@ -68,6 +68,46 @@ MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress, return LockChange; } +ULONG +NTAPI +MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress, + IN KIRQL OldIrql) +{ + NTSTATUS Status; + BOOLEAN LockChange = FALSE; + + /* Must be e kernel address */ + ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS); + + /* Check if the page is valid */ + while (!MmIsAddressValid(VirtualAddress)) + { + /* Release the PFN database */ + KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); + + /* Fault it in */ + Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL); + if (!NT_SUCCESS(Status)) + { + /* This should not fail */ + KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR, + 3, + Status, + 0, + (ULONG_PTR)VirtualAddress); + } + + /* This flag will be useful later when we do better locking */ + LockChange = TRUE; + + /* Lock the PFN database */ + OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); + } + + /* Let caller know what the lock state is */ + return LockChange; +} + PFN_NUMBER NTAPI MiDeleteSystemPageableVm(IN PMMPTE PointerPte, @@ -169,11 +209,13 @@ VOID NTAPI MiDeletePte(IN PMMPTE PointerPte, IN PVOID VirtualAddress, - IN PEPROCESS CurrentProcess) + IN PEPROCESS CurrentProcess, + IN PMMPTE PrototypePte) { PMMPFN Pfn1; MMPTE TempPte; PFN_NUMBER PageFrameIndex; + PMMPDE PointerPde; /* PFN lock must be held */ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); @@ -183,39 +225,74 @@ MiDeletePte(IN PMMPTE PointerPte, /* We only support valid PTEs for now */ ASSERT(TempPte.u.Hard.Valid == 1); - ASSERT(TempPte.u.Soft.Prototype == 0); - ASSERT(TempPte.u.Soft.Transition == 0); + if (TempPte.u.Hard.Valid == 0) + { + /* Invalid PTEs not supported yet */ + ASSERT(TempPte.u.Soft.Prototype == 0); + ASSERT(TempPte.u.Soft.Transition == 0); + } /* Get the PFN entry */ PageFrameIndex = PFN_FROM_PTE(&TempPte); Pfn1 = MiGetPfnEntry(PageFrameIndex); - /* We don't support deleting prototype PTEs for now */ - ASSERT(Pfn1->u3.e1.PrototypePte == 0); - - /* Make sure the saved PTE address is valid */ - if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) + /* Check if this is a valid, prototype PTE */ + if (Pfn1->u3.e1.PrototypePte == 1) { - /* The PFN entry is illegal, or invalid */ - KeBugCheckEx(MEMORY_MANAGEMENT, - 0x401, - (ULONG_PTR)PointerPte, - PointerPte->u.Long, - (ULONG_PTR)Pfn1->PteAddress); + /* Get the PDE and make sure it's faulted in */ + PointerPde = MiAddressToPde(PointerPte); + if (PointerPde->u.Hard.Valid == 0) + { +#if (_MI_PAGING_LEVELS == 2) + /* Could be paged pool access from a new process -- synchronize the page directories */ + if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress))) + { +#endif + /* The PDE must be valid at this point */ + KeBugCheckEx(MEMORY_MANAGEMENT, + 0x61940, + (ULONG_PTR)PointerPte, + PointerPte->u.Long, + (ULONG_PTR)VirtualAddress); + } +#if (_MI_PAGING_LEVELS == 2) + } +#endif + /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */ + //MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PDE(PointerPde)); + + /* Drop the share count */ + MiDecrementShareCount(Pfn1, PageFrameIndex); + + /* No fork yet */ + if (PointerPte <= MiHighestUserPte) ASSERT(PrototypePte == Pfn1->PteAddress); } + else + { + /* Make sure the saved PTE address is valid */ + if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) + { + /* The PFN entry is illegal, or invalid */ + KeBugCheckEx(MEMORY_MANAGEMENT, + 0x401, + (ULONG_PTR)PointerPte, + PointerPte->u.Long, + (ULONG_PTR)Pfn1->PteAddress); + } - /* There should only be 1 shared reference count */ - ASSERT(Pfn1->u2.ShareCount == 1); + /* There should only be 1 shared reference count */ + ASSERT(Pfn1->u2.ShareCount == 1); - /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */ - //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); + /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */ + //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); - /* Mark the PFN for deletion and dereference what should be the last ref */ - MI_SET_PFN_DELETED(Pfn1); - MiDecrementShareCount(Pfn1, PageFrameIndex); + /* Mark the PFN for deletion and dereference what should be the last ref */ + MI_SET_PFN_DELETED(Pfn1); + MiDecrementShareCount(Pfn1, PageFrameIndex); - /* We should eventually do this */ - //CurrentProcess->NumberOfPrivatePages--; + /* We should eventually do this */ + //CurrentProcess->NumberOfPrivatePages--; + } /* Destroy the PTE and flush the TLB */ PointerPte->u.Long = 0; @@ -228,27 +305,34 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va, IN ULONG_PTR EndingAddress, IN PMMVAD Vad) { - PMMPTE PointerPte, PointerPde; + PMMPTE PointerPte, PointerPde, PrototypePte, LastPrototypePte; MMPTE TempPte; PEPROCESS CurrentProcess; KIRQL OldIrql; + BOOLEAN AddressGap = FALSE; + PSUBSECTION Subsection; + + /* Get out if this is a fake VAD, RosMm will free the marea pages */ + if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return; /* Grab the process and PTE/PDE for the address being deleted */ CurrentProcess = PsGetCurrentProcess(); PointerPde = MiAddressToPde(Va); PointerPte = MiAddressToPte(Va); - /* We usually only get a VAD when it's not a VM address */ - if (Vad) + /* Check if this is a section VAD or a VM VAD */ + if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte)) { - /* Get out if this is a fake VAD, RosMm will free the marea pages */ - if (Vad->u.VadFlags.Spare == 1) return; - - /* At process deletion, we may get a VAD, but it should be a VM VAD */ - ASSERT(Vad->u.VadFlags.PrivateMemory); - //ASSERT(Vad->FirstPrototypePte == NULL); memory_area fuckers + /* Don't worry about prototypes */ + PrototypePte = LastPrototypePte = NULL; } - + else + { + /* Get the prototype PTE */ + PrototypePte = Vad->FirstPrototypePte; + LastPrototypePte = Vad->FirstPrototypePte + 1; + } + /* In all cases, we don't support fork() yet */ ASSERT(CurrentProcess->CloneRoot == NULL); @@ -256,8 +340,11 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va, while (TRUE) { /* First keep going until we find a valid PDE */ - while (PointerPde->u.Long == 0) + while (!PointerPde->u.Long) { + /* There are gaps in the address space */ + AddressGap = TRUE; + /* Still no valid PDE, try the next 4MB (or whatever) */ PointerPde++; @@ -270,7 +357,7 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va, } /* Now check if the PDE is mapped in */ - if (PointerPde->u.Hard.Valid == 0) + if (!PointerPde->u.Hard.Valid) { /* It isn't, so map it in */ PointerPte = MiPteToAddress(PointerPde); @@ -281,6 +368,26 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va, ASSERT(PointerPde->u.Hard.Valid == 1); ASSERT(Va <= EndingAddress); + /* Check if this is a section VAD with gaps in it */ + if ((AddressGap) && (LastPrototypePte)) + { + /* We need to skip to the next correct prototype PTE */ + PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); + + /* And we need the subsection to skip to the next last prototype PTE */ + Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); + if (Subsection) + { + /* Found it! */ + LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; + } + else + { + /* No more subsections, we are done with prototype PTEs */ + PrototypePte = NULL; + } + } + /* Lock the PFN Database while we delete the PTEs */ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); do @@ -292,11 +399,41 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va, /* Check if the PTE is actually mapped in */ if (TempPte.u.Long & 0xFFFFFC01) { - /* It is, we don't support prototype PTEs for now though */ - ASSERT(TempPte.u.Soft.Prototype == 0); + /* Are we dealing with section VAD? */ + if ((LastPrototypePte) && (PrototypePte > LastPrototypePte)) + { + /* We need to skip to the next correct prototype PTE */ + PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); + + /* And we need the subsection to skip to the next last prototype PTE */ + Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); + if (Subsection) + { + /* Found it! */ + LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; + } + else + { + /* No more subsections, we are done with prototype PTEs */ + PrototypePte = NULL; + } + } - /* Delete the PTE proper */ - MiDeletePte(PointerPte, (PVOID)Va, CurrentProcess); + /* Check for prototype PTE */ + if ((TempPte.u.Hard.Valid == 0) && + (TempPte.u.Soft.Prototype == 1)) + { + /* Just nuke it */ + PointerPte->u.Long = 0; + } + else + { + /* Delete the PTE proper */ + MiDeletePte(PointerPte, + (PVOID)Va, + CurrentProcess, + PrototypePte); + } } else { @@ -308,6 +445,7 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va, /* Update the address and PTE for it */ Va += PAGE_SIZE; PointerPte++; + PrototypePte++; /* Making sure the PDE is still valid */ ASSERT(PointerPde->u.Hard.Valid == 1); @@ -323,6 +461,7 @@ MiDeleteVirtualAddresses(IN ULONG_PTR Va, /* Otherwise, we exited because we hit a new PDE boundary, so start over */ PointerPde = MiAddressToPde(Va); + AddressGap = FALSE; } }