[NTOS/MM]

- Implement copy on write support in ARM3 page fault handler
CORE-8541 #resolve #comment committed in r72395

svn path=/trunk/; revision=72395
This commit is contained in:
Jérôme Gardou 2016-08-19 17:24:53 +00:00
parent fa041e04ad
commit 690941472d
4 changed files with 203 additions and 34 deletions

View file

@ -880,6 +880,18 @@ MI_IS_MAPPED_PTE(PMMPTE PointerPte)
#endif
FORCEINLINE
VOID
MI_MAKE_TRANSITION_PTE(_Out_ PMMPTE NewPte,
_In_ PFN_NUMBER Page,
_In_ ULONG Protection)
{
NewPte->u.Long = 0;
NewPte->u.Trans.Transition = 1;
NewPte->u.Trans.Protection = Protection;
NewPte->u.Trans.PageFrameNumber = Page;
}
//
// Returns if the page is physically resident (ie: a large page)
// FIXFIX: CISC/x86 only?
@ -2169,6 +2181,15 @@ MiDeleteVirtualAddresses(
IN PMMVAD Vad
);
VOID
NTAPI
MiDeletePte(
IN PMMPTE PointerPte,
IN PVOID VirtualAddress,
IN PEPROCESS CurrentProcess,
IN PMMPTE PrototypePte
);
ULONG
NTAPI
MiMakeSystemAddressValid(

View file

@ -521,6 +521,71 @@ MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
}
VOID
NTAPI
MiCopyPfn(
_In_ PFN_NUMBER DestPage,
_In_ PFN_NUMBER SrcPage)
{
PMMPTE SysPtes;
MMPTE TempPte;
PMMPFN DestPfn, SrcPfn;
PVOID DestAddress;
const VOID* SrcAddress;
/* Get the PFNs */
DestPfn = MiGetPfnEntry(DestPage);
ASSERT(DestPfn);
SrcPfn = MiGetPfnEntry(SrcPage);
ASSERT(SrcPfn);
/* Grab 2 system PTEs */
SysPtes = MiReserveSystemPtes(2, SystemPteSpace);
ASSERT(SysPtes);
/* Initialize the destination PTE */
TempPte = ValidKernelPte;
TempPte.u.Hard.PageFrameNumber = DestPage;
/* Setup caching */
if (DestPfn->u3.e1.CacheAttribute == MiWriteCombined)
{
/* Write combining, no caching */
MI_PAGE_DISABLE_CACHE(&TempPte);
MI_PAGE_WRITE_COMBINED(&TempPte);
}
else if (DestPfn->u3.e1.CacheAttribute == MiNonCached)
{
/* Write through, no caching */
MI_PAGE_DISABLE_CACHE(&TempPte);
MI_PAGE_WRITE_THROUGH(&TempPte);
}
/* Make the system PTE valid with our PFN */
MI_WRITE_VALID_PTE(&SysPtes[0], TempPte);
/* Initialize the source PTE */
TempPte = ValidKernelPte;
TempPte.u.Hard.PageFrameNumber = SrcPage;
/* Setup caching */
if (SrcPfn->u3.e1.CacheAttribute == MiNonCached)
{
MI_PAGE_DISABLE_CACHE(&TempPte);
}
/* Make the system PTE valid with our PFN */
MI_WRITE_VALID_PTE(&SysPtes[1], TempPte);
/* Get the addresses and perform the copy */
DestAddress = MiPteToAddress(&SysPtes[0]);
SrcAddress = MiPteToAddress(&SysPtes[1]);
RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
/* Now get rid of it */
MiReleaseSystemPtes(SysPtes, 2, SystemPteSpace);
}
NTSTATUS
NTAPI
MiResolveDemandZeroFault(IN PVOID Address,
@ -1043,6 +1108,7 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
PFN_NUMBER PageFrameIndex;
NTSTATUS Status;
PVOID InPageBlock = NULL;
ULONG Protection;
/* Must be called with an invalid, prototype PTE, with the PFN lock held */
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
@ -1088,31 +1154,95 @@ MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
{
if (!PteContents.u.Proto.ReadOnly)
{
/* Check for page acess in software */
Status = MiAccessCheck(PointerProtoPte,
StoreInstruction,
KernelMode,
TempPte.u.Soft.Protection,
TrapInformation,
TRUE);
ASSERT(Status == STATUS_SUCCESS);
/* Check for copy on write page */
if ((TempPte.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
{
/* Not yet supported */
ASSERT(FALSE);
}
Protection = TempPte.u.Soft.Protection;
}
else
{
Protection = MM_READONLY;
}
/* Check for page acess in software */
Status = MiAccessCheck(PointerProtoPte,
StoreInstruction,
KernelMode,
TempPte.u.Soft.Protection,
TrapInformation,
TRUE);
ASSERT(Status == STATUS_SUCCESS);
}
else
{
/* Check for copy on write page */
if ((PteContents.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
Protection = PteContents.u.Soft.Protection;
}
/* Check for writing copy on write page */
if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction)
{
PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex;
ULONG Color;
/* Resolve the proto fault as if it was a read operation */
Status = MiResolveProtoPteFault(FALSE,
Address,
PointerPte,
PointerProtoPte,
OutPfn,
PageFileData,
PteValue,
Process,
OldIrql,
TrapInformation);
if (!NT_SUCCESS(Status))
{
/* Not yet supported */
ASSERT(FALSE);
return Status;
}
/* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
/* And re-read the proto PTE */
TempPte = *PointerProtoPte;
ASSERT(TempPte.u.Hard.Valid == 1);
ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte);
/* Get a new page for the private copy */
if (Process > HYDRA_PROCESS)
Color = MI_GET_NEXT_PROCESS_COLOR(Process);
else
Color = MI_GET_NEXT_COLOR();
PageFrameIndex = MiRemoveAnyPage(Color);
/* Perform the copy */
MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex);
/* This will drop everything MiResolveProtoPteFault referenced */
MiDeletePte(PointerPte, Address, Process, PointerProtoPte);
/* Because now we use this */
Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
/* Fix the protection */
Protection &= ~MM_WRITECOPY;
Protection |= MM_READWRITE;
if (Address < MmSystemRangeStart)
{
/* Build the user PTE */
MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection, PageFrameIndex);
}
else
{
/* Build the kernel PTE */
MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection, PageFrameIndex);
}
/* And finally, write the valid PTE */
MI_WRITE_VALID_PTE(PointerPte, PteContents);
/* The caller expects us to release the PFN lock */
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
return Status;
}
/* Check for clone PTEs */
@ -2002,8 +2132,39 @@ UserFault:
/* Is this a copy on write PTE? */
if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
{
/* Not supported yet */
ASSERT(FALSE);
PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
PMMPFN Pfn1;
LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
ASSERT(MmAvailablePages > 0);
/* Allocate a new page and copy it */
PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
/* Dereference whatever this PTE is referencing */
Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
ASSERT(Pfn1->u3.e1.PrototypePte == 1);
ASSERT(!MI_IS_PFN_DELETED(Pfn1));
ProtoPte = Pfn1->PteAddress;
MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
/* And make a new shiny one with our page */
MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
TempPte.u.Hard.Write = 1;
TempPte.u.Hard.CopyOnWrite = 0;
MI_WRITE_VALID_PTE(PointerPte, TempPte);
KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
/* Return the status */
MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
return STATUS_PAGE_FAULT_COPY_ON_WRITE;
}
/* Is this a read-only PTE? */

View file

@ -229,13 +229,6 @@ MiIsBalancerThread(VOID)
(PsGetCurrentThreadId() == MiBalancerThreadId.UniqueThread);
}
VOID
NTAPI
MiDeletePte(IN PMMPTE PointerPte,
IN PVOID VirtualAddress,
IN PEPROCESS CurrentProcess,
IN PMMPTE PrototypePte);
VOID
NTAPI
MmRebalanceMemoryConsumers(VOID)

View file

@ -275,12 +275,6 @@ MiRemoveNode(IN PMMADDRESS_NODE Node,
*
* @remarks Lock the address space before calling this function.
*/
VOID
NTAPI
MiDeletePte(IN PMMPTE PointerPte,
IN PVOID VirtualAddress,
IN PEPROCESS CurrentProcess,
IN PMMPTE PrototypePte);
NTSTATUS NTAPI
MmFreeMemoryArea(