*/
/* INCLUDES *******************************************************************/
+/* So long, and Thanks for All the Fish */
#include <ntoskrnl.h>
#define NDEBUG
if (Vad->u.VadFlags.MemCommit == 1)
{
/* This is a committed VAD, so Assume the whole range is committed */
- CommittedPages = BYTES_TO_PAGES(EndingAddress - StartingAddress);
+ CommittedPages = (ULONG)BYTES_TO_PAGES(EndingAddress - StartingAddress);
/* Is the PDE demand-zero? */
PointerPde = MiAddressToPte(PointerPte);
IN PEPROCESS CurrentProcess)
{
NTSTATUS Status;
- BOOLEAN WsWasLocked = FALSE, LockChange = FALSE;
+ BOOLEAN WsShared = FALSE, WsSafe = FALSE, LockChange = FALSE;
PETHREAD CurrentThread = PsGetCurrentThread();
/* Must be a non-pool page table, since those are double-mapped already */
/* Check if the page table is valid */
while (!MmIsAddressValid(PageTableVirtualAddress))
{
- /* Check if the WS is locked */
- if (CurrentThread->OwnsProcessWorkingSetExclusive)
- {
- /* Unlock the working set and remember it was locked */
- MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
- WsWasLocked = TRUE;
- }
+ /* Release the working set lock */
+ MiUnlockProcessWorkingSetForFault(CurrentProcess,
+ CurrentThread,
+ &WsSafe,
+ &WsShared);
/* Fault it in */
Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
}
/* Lock the working set again */
- if (WsWasLocked) MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
+ MiLockProcessWorkingSetForFault(CurrentProcess,
+ CurrentThread,
+ WsSafe,
+ WsShared);
/* This flag will be useful later when we do better locking */
LockChange = TRUE;
KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
/* Destroy the PTE */
- PointerPte->u.Long = 0;
+ MI_ERASE_PTE(PointerPte);
+ }
+ else
+ {
+ /*
+ * The only other ARM3 possibility is a demand zero page, which would
+ * mean freeing some of the paged pool pages that haven't even been
+ * touched yet, as part of a larger allocation.
+ *
+ * Right now, we shouldn't expect any page file information in the PTE
+ */
+ ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
+
+ /* Destroy the PTE */
+ MI_ERASE_PTE(PointerPte);
}
/* Actual legitimate pages */
ActualPages++;
}
- else
- {
- /*
- * The only other ARM3 possibility is a demand zero page, which would
- * mean freeing some of the paged pool pages that haven't even been
- * touched yet, as part of a larger allocation.
- *
- * Right now, we shouldn't expect any page file information in the PTE
- */
- ASSERT(PointerPte->u.Soft.PageFileHigh == 0);
-
- /* Destroy the PTE */
- PointerPte->u.Long = 0;
- }
/* Keep going */
PointerPte++;
}
/* Destroy the PTE and flush the TLB */
- PointerPte->u.Long = 0;
+ MI_ERASE_PTE(PointerPte);
KeFlushCurrentTb();
}
KIRQL OldIrql;
BOOLEAN AddressGap = FALSE;
PSUBSECTION Subsection;
- PUSHORT UsedPageTableEntries;
/* Get out if this is a fake VAD, RosMm will free the marea pages */
if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
/* Now we should have a valid PDE, mapped in, and still have some VA */
ASSERT(PointerPde->u.Hard.Valid == 1);
ASSERT(Va <= EndingAddress);
- UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Va)];
/* Check if this is a section VAD with gaps in it */
if ((AddressGap) && (LastPrototypePte))
TempPte = *PointerPte;
if (TempPte.u.Long)
{
- DPRINT("Decrement used PTEs by address: %lx\n", Va);
- (*UsedPageTableEntries)--;
- ASSERT((*UsedPageTableEntries) < PTE_COUNT);
- DPRINT("Refs: %lx\n", (*UsedPageTableEntries));
+ MiDecrementPageTableReferences((PVOID)Va);
/* Check if the PTE is actually mapped in */
- if (TempPte.u.Long & 0xFFFFFC01)
+ if (MI_IS_MAPPED_PTE(&TempPte))
{
/* Are we dealing with section VAD? */
if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
(TempPte.u.Soft.Prototype == 1))
{
/* Just nuke it */
- PointerPte->u.Long = 0;
+ MI_ERASE_PTE(PointerPte);
}
else
{
else
{
/* The PTE was never mapped, just nuke it here */
- PointerPte->u.Long = 0;
+ MI_ERASE_PTE(PointerPte);
}
}
/* The PDE should still be valid at this point */
ASSERT(PointerPde->u.Hard.Valid == 1);
- DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va, PointerPde->u.Hard.PageFrameNumber);
- if (!(*UsedPageTableEntries))
+ /* Check remaining PTE count (go back 1 page due to above loop) */
+ if (MiQueryPageTableReferences((PVOID)(Va - PAGE_SIZE)) == 0)
{
- DPRINT("They are!\n");
if (PointerPde->u.Long != 0)
{
- DPRINT("PDE active: %lx in %16s\n", PointerPde->u.Hard.PageFrameNumber, CurrentProcess->ImageFileName);
-
/* Delete the PTE proper */
MiDeletePte(PointerPde,
MiPteToAddress(PointerPde),
//
// Return the error
//
- return STATUS_WORKING_SET_QUOTA;
+ _SEH2_YIELD(return STATUS_WORKING_SET_QUOTA);
}
//
//
// Check if we had allocated pool
//
- if (HavePoolAddress) ExFreePool(PoolAddress);
+ if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
//
// Check if we failed during the probe
//
// Check if we had allocated pool
//
- if (HavePoolAddress) ExFreePool(PoolAddress);
+ if (HavePoolAddress) ExFreePoolWithTag(PoolAddress, 'VmRw');
//
// All bytes read
{
MMPTE TempPte;
PMMPFN Pfn;
+ PEPROCESS CurrentProcess;
+ PETHREAD CurrentThread;
+ BOOLEAN WsSafe, WsShared;
+ ULONG Protect;
+ KIRQL OldIrql;
PAGED_CODE();
/* Copy this PTE's contents */
ASSERT(TempPte.u.Long);
/* Check for a special prototype format */
- if (TempPte.u.Soft.Valid == 0 &&
- TempPte.u.Soft.Prototype == 1)
+ if ((TempPte.u.Soft.Valid == 0) &&
+ (TempPte.u.Soft.Prototype == 1))
{
- /* Unsupported now */
- UNIMPLEMENTED;
- ASSERT(FALSE);
+ /* Check if the prototype PTE is not yet pointing to a PTE */
+ if (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
+ {
+ /* The prototype PTE contains the protection */
+ return MmProtectToValue[TempPte.u.Soft.Protection];
+ }
+
+ /* Get a pointer to the underlying shared PTE */
+ PointerPte = MiProtoPteToPte(&TempPte);
+
+ /* Since the PTE we want to read can be paged out at any time, we need
+ to release the working set lock first, so that it can be paged in */
+ CurrentThread = PsGetCurrentThread();
+ CurrentProcess = PsGetCurrentProcess();
+ MiUnlockProcessWorkingSetForFault(CurrentProcess,
+ CurrentThread,
+ &WsSafe,
+ &WsShared);
+
+ /* Now read the PTE value */
+ TempPte = *PointerPte;
+
+ /* Check if that one is invalid */
+ if (!TempPte.u.Hard.Valid)
+ {
+ /* We get the protection directly from this PTE */
+ Protect = MmProtectToValue[TempPte.u.Soft.Protection];
+ }
+ else
+ {
+ /* The PTE is valid, so we might need to get the protection from
+ the PFN. Lock the PFN database */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ /* Check if the PDE is still valid */
+ if (MiAddressToPte(PointerPte)->u.Hard.Valid == 0)
+ {
+ /* It's not, make it valid */
+ MiMakeSystemAddressValidPfn(PointerPte, OldIrql);
+ }
+
+ /* Now it's safe to read the PTE value again */
+ TempPte = *PointerPte;
+ ASSERT(TempPte.u.Long != 0);
+
+ /* Check again if the PTE is invalid */
+ if (!TempPte.u.Hard.Valid)
+ {
+ /* The PTE is not valid, so we can use it's protection field */
+ Protect = MmProtectToValue[TempPte.u.Soft.Protection];
+ }
+ else
+ {
+ /* The PTE is valid, so we can find the protection in the
+ OriginalPte field of the PFN */
+ Pfn = MI_PFN_ELEMENT(TempPte.u.Hard.PageFrameNumber);
+ Protect = MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection];
+ }
+
+ /* Release the PFN database */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ }
+
+ /* Lock the working set again */
+ MiLockProcessWorkingSetForFault(CurrentProcess,
+ CurrentThread,
+ WsSafe,
+ WsShared);
+
+ return Protect;
}
/* In the easy case of transition or demand zero PTE just return its protection */
}
/* This is software PTE */
- DPRINT1("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
- DPRINT1("VA: %p\n", MiPteToAddress(&TempPte));
- DPRINT1("Mask: %lx\n", TempPte.u.Soft.Protection);
- DPRINT1("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
+ DPRINT("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn);
+ DPRINT("VA: %p\n", MiPteToAddress(&TempPte));
+ DPRINT("Mask: %lx\n", TempPte.u.Soft.Protection);
+ DPRINT("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection);
return MmProtectToValue[TempPte.u.Soft.Protection];
}
PMMPTE PointerPte, ProtoPte;
PMMPDE PointerPde;
+#if (_MI_PAGING_LEVELS >= 3)
+ PMMPPE PointerPpe;
+#endif
+#if (_MI_PAGING_LEVELS >= 4)
+ PMMPXE PointerPxe;
+#endif
MMPTE TempPte, TempProtoPte;
BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
ULONG State = MEM_RESERVE, Protect = 0;
/* Get the PDE and PTE for the address */
PointerPde = MiAddressToPde(Va);
PointerPte = MiAddressToPte(Va);
+#if (_MI_PAGING_LEVELS >= 3)
+ PointerPpe = MiAddressToPpe(Va);
+#endif
+#if (_MI_PAGING_LEVELS >= 4)
+ PointerPxe = MiAddressToPxe(Va);
+#endif
/* Return the next range */
*NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
- /* Is the PDE demand-zero? */
- if (PointerPde->u.Long != 0)
+ do
{
- /* It is not. Is it valid? */
+#if (_MI_PAGING_LEVELS >= 4)
+ /* Does the PXE exist? */
+ if (PointerPxe->u.Long == 0)
+ {
+ /* It does not, next range starts at the next PXE */
+ *NextVa = MiPxeToAddress(PointerPxe + 1);
+ break;
+ }
+
+ /* Is the PXE valid? */
+ if (PointerPxe->u.Hard.Valid == 0)
+ {
+ /* Is isn't, fault it in (make the PPE accessible) */
+ MiMakeSystemAddressValid(PointerPpe, TargetProcess);
+ }
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+ /* Does the PPE exist? */
+ if (PointerPpe->u.Long == 0)
+ {
+ /* It does not, next range starts at the next PPE */
+ *NextVa = MiPpeToAddress(PointerPpe + 1);
+ break;
+ }
+
+ /* Is the PPE valid? */
+ if (PointerPpe->u.Hard.Valid == 0)
+ {
+ /* Is isn't, fault it in (make the PDE accessible) */
+ MiMakeSystemAddressValid(PointerPde, TargetProcess);
+ }
+#endif
+
+ /* Does the PDE exist? */
+ if (PointerPde->u.Long == 0)
+ {
+ /* It does not, next range starts at the next PDE */
+ *NextVa = MiPdeToAddress(PointerPde + 1);
+ break;
+ }
+
+ /* Is the PDE valid? */
if (PointerPde->u.Hard.Valid == 0)
{
- /* Is isn't, fault it in */
- PointerPte = MiPteToAddress(PointerPde);
+ /* Is isn't, fault it in (make the PTE accessible) */
MiMakeSystemAddressValid(PointerPte, TargetProcess);
- ValidPte = TRUE;
}
- }
- else
- {
- /* It is, skip it and move to the next PDE */
- *NextVa = MiPdeToAddress(PointerPde + 1);
- }
+
+ /* We have a PTE that we can access now! */
+ ValidPte = TRUE;
+
+ } while (FALSE);
/* Is it safe to try reading the PTE? */
if (ValidPte)
KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
}
+ /* Lock the address space and make sure the process isn't already dead */
+ MmLockAddressSpace(&TargetProcess->Vm);
+ if (TargetProcess->VmDeleted)
+ {
+ /* Unlock the address space of the process */
+ MmUnlockAddressSpace(&TargetProcess->Vm);
+
+ /* Check if we were attached */
+ if (ProcessHandle != NtCurrentProcess())
+ {
+ /* Detach and dereference the process */
+ KeUnstackDetachProcess(&ApcState);
+ ObDereferenceObject(TargetProcess);
+ }
+
+ /* Bail out */
+ DPRINT1("Process is dying\n");
+ return STATUS_PROCESS_IS_TERMINATING;
+ }
+
/* Loop the VADs */
ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
if (TargetProcess->VadRoot.NumberGenericTableElements)
MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address;
}
+ /* Unlock the address space of the process */
+ MmUnlockAddressSpace(&TargetProcess->Vm);
+
/* Check if we were attached */
if (ProcessHandle != NtCurrentProcess())
{
MemoryInfo.Type = MEM_MAPPED;
}
- /* Lock the address space of the process */
- MmLockAddressSpace(&TargetProcess->Vm);
-
/* Find the memory area the specified address belongs to */
MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress);
ASSERT(MemoryArea != NULL);
if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
{
Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength);
- ASSERT(NT_SUCCESS(Status));
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("MmQuerySectionView failed. MemoryArea=%p (%p-%p), BaseAddress=%p\n",
+ MemoryArea, MemoryArea->StartingAddress, MemoryArea->EndingAddress, BaseAddress);
+ NT_ASSERT(NT_SUCCESS(Status));
+ }
}
else
{
MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
MemoryInfo.Type = MEM_PRIVATE;
+ /* Acquire the working set lock (shared is enough) */
+ MiLockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
+
/* Find the largest chunk of memory which has the same state and protection mask */
MemoryInfo.State = MiQueryAddressState(Address,
Vad,
Address = NextAddress;
}
+ /* Release the working set lock */
+ MiUnlockProcessWorkingSetShared(TargetProcess, PsGetCurrentThread());
+
+ /* Check if we went outside of the VAD */
+ if (((ULONG_PTR)Address >> PAGE_SHIFT) > Vad->EndingVpn)
+ {
+ /* Set the end of the VAD as the end address */
+ Address = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
+ }
+
/* Now that we know the last VA address, calculate the region size */
MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
}
ObDereferenceObject(TargetProcess);
}
- /* Return the data, NtQueryInformation already probed it*/
+ /* Return the data, NtQueryInformation already probed it */
if (PreviousMode != KernelMode)
{
_SEH2_TRY
return Status;
}
+BOOLEAN
+NTAPI
+MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
+ IN ULONG_PTR EndingAddress,
+ IN PMMVAD Vad,
+ IN PEPROCESS Process)
+{
+ PMMPTE PointerPte, LastPte, PointerPde;
+ BOOLEAN OnBoundary = TRUE;
+ PAGED_CODE();
+
+ /* Get the PDE and PTE addresses */
+ PointerPde = MiAddressToPde(StartingAddress);
+ PointerPte = MiAddressToPte(StartingAddress);
+ LastPte = MiAddressToPte(EndingAddress);
+
+ /* Loop all the PTEs */
+ while (PointerPte <= LastPte)
+ {
+ /* Check if we've hit an new PDE boundary */
+ if (OnBoundary)
+ {
+ /* Is this PDE demand zero? */
+ PointerPde = MiAddressToPte(PointerPte);
+ if (PointerPde->u.Long != 0)
+ {
+ /* It isn't -- is it valid? */
+ if (PointerPde->u.Hard.Valid == 0)
+ {
+ /* Nope, fault it in */
+ PointerPte = MiPteToAddress(PointerPde);
+ MiMakeSystemAddressValid(PointerPte, Process);
+ }
+ }
+ else
+ {
+ /* The PTE was already valid, so move to the next one */
+ PointerPde++;
+ PointerPte = MiPteToAddress(PointerPde);
+
+ /* Is the entire VAD committed? If not, fail */
+ if (!Vad->u.VadFlags.MemCommit) return FALSE;
+
+ /* Everything is committed so far past the range, return true */
+ if (PointerPte > LastPte) return TRUE;
+ }
+ }
+
+ /* Is the PTE demand zero? */
+ if (PointerPte->u.Long == 0)
+ {
+ /* Is the entire VAD committed? If not, fail */
+ if (!Vad->u.VadFlags.MemCommit) return FALSE;
+ }
+ else
+ {
+ /* It isn't -- is it a decommited, invalid, or faulted PTE? */
+ if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) &&
+ (PointerPte->u.Hard.Valid == 0) &&
+ ((PointerPte->u.Soft.Prototype == 0) ||
+ (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)))
+ {
+ /* Then part of the range is decommitted, so fail */
+ return FALSE;
+ }
+ }
+
+ /* Move to the next PTE */
+ PointerPte++;
+ OnBoundary = MiIsPteOnPdeBoundary(PointerPte);
+ }
+
+ /* All PTEs seem valid, and no VAD checks failed, the range is okay */
+ return TRUE;
+}
+
+NTSTATUS
+NTAPI
+MiRosProtectVirtualMemory(IN PEPROCESS Process,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T NumberOfBytesToProtect,
+ IN ULONG NewAccessProtection,
+ OUT PULONG OldAccessProtection OPTIONAL)
+{
+ PMEMORY_AREA MemoryArea;
+ PMMSUPPORT AddressSpace;
+ ULONG OldAccessProtection_;
+ NTSTATUS Status;
+
+ *NumberOfBytesToProtect = PAGE_ROUND_UP((ULONG_PTR)(*BaseAddress) + (*NumberOfBytesToProtect)) - PAGE_ROUND_DOWN(*BaseAddress);
+ *BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
+
+ AddressSpace = &Process->Vm;
+ MmLockAddressSpace(AddressSpace);
+ MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
+ if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
+ {
+ MmUnlockAddressSpace(AddressSpace);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (OldAccessProtection == NULL) OldAccessProtection = &OldAccessProtection_;
+
+ ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
+ Status = MmProtectSectionView(AddressSpace,
+ MemoryArea,
+ *BaseAddress,
+ *NumberOfBytesToProtect,
+ NewAccessProtection,
+ OldAccessProtection);
+
+ MmUnlockAddressSpace(AddressSpace);
+
+ return Status;
+}
+
NTSTATUS
NTAPI
MiProtectVirtualMemory(IN PEPROCESS Process,
ULONG_PTR StartingAddress, EndingAddress;
PMMPTE PointerPde, PointerPte, LastPte;
MMPTE PteContents;
- PUSHORT UsedPageTableEntries;
PMMPFN Pfn1;
- ULONG ProtectionMask;
+ ULONG ProtectionMask, OldProtect;
+ BOOLEAN Committed;
NTSTATUS Status = STATUS_SUCCESS;
+ PETHREAD Thread = PsGetCurrentThread();
+ TABLE_SEARCH_RESULT Result;
- /* Check for ROS specific memory area */
- MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
- if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
- {
- return MiRosProtectVirtualMemory(Process,
- BaseAddress,
- NumberOfBytesToProtect,
- NewAccessProtection,
- OldAccessProtection);
- }
-
- /* Calcualte base address for the VAD */
+ /* Calculate base address for the VAD */
StartingAddress = (ULONG_PTR)PAGE_ALIGN((*BaseAddress));
EndingAddress = (((ULONG_PTR)*BaseAddress + *NumberOfBytesToProtect - 1) | (PAGE_SIZE - 1));
return STATUS_INVALID_PAGE_PROTECTION;
}
+ /* Check for ROS specific memory area */
+ MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
+ if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
+ {
+ /* Evil hack */
+ return MiRosProtectVirtualMemory(Process,
+ BaseAddress,
+ NumberOfBytesToProtect,
+ NewAccessProtection,
+ OldAccessProtection);
+ }
+
/* Lock the address space and make sure the process isn't already dead */
AddressSpace = MmGetCurrentAddressSpace();
MmLockAddressSpace(AddressSpace);
}
/* Get the VAD for this address range, and make sure it exists */
- Vad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
- EndingAddress >> PAGE_SHIFT,
- &Process->VadRoot);
- if (!Vad)
+ Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
+ EndingAddress >> PAGE_SHIFT,
+ &Process->VadRoot,
+ (PMMADDRESS_NODE*)&Vad);
+ if (Result != TableFoundNode)
{
DPRINT("Could not find a VAD for this allocation\n");
Status = STATUS_CONFLICTING_ADDRESSES;
goto FailPath;
}
+ /* Is this section, or private memory? */
if (Vad->u.VadFlags.PrivateMemory == 0)
{
- /* This is a section, handled by the ROS specific code above */
- UNIMPLEMENTED;
+ /* Not yet supported */
+ if (Vad->u.VadFlags.VadType == VadLargePageSection)
+ {
+ DPRINT1("Illegal VAD for attempting to set protection\n");
+ Status = STATUS_CONFLICTING_ADDRESSES;
+ goto FailPath;
+ }
+
+ /* Rotate VADs are not yet supported */
+ if (Vad->u.VadFlags.VadType == VadRotatePhysical)
+ {
+ DPRINT1("Illegal VAD for attempting to set protection\n");
+ Status = STATUS_CONFLICTING_ADDRESSES;
+ goto FailPath;
+ }
+
+ /* Not valid on section files */
+ if (NewAccessProtection & (PAGE_NOCACHE | PAGE_WRITECOMBINE))
+ {
+ /* Fail */
+ DPRINT1("Invalid protection flags for section\n");
+ Status = STATUS_INVALID_PARAMETER_4;
+ goto FailPath;
+ }
+
+ /* Check if data or page file mapping protection PTE is compatible */
+ if (!Vad->ControlArea->u.Flags.Image)
+ {
+ /* Not yet */
+ DPRINT1("Fixme: Not checking for valid protection\n");
+ }
+
+ /* This is a section, and this is not yet supported */
+ DPRINT1("Section protection not yet supported\n");
+ OldProtect = 0;
}
else
{
if ((NewAccessProtection & PAGE_WRITECOPY) ||
(NewAccessProtection & PAGE_EXECUTE_WRITECOPY))
{
+ DPRINT1("Invalid protection flags for private memory\n");
Status = STATUS_INVALID_PARAMETER_4;
goto FailPath;
}
- //MiLockProcessWorkingSet(Thread, Process);
+ /* Lock the working set */
+ MiLockProcessWorkingSetUnsafe(Process, Thread);
- /* TODO: Check if all pages in this range are committed */
+ /* Check if all pages in this range are committed */
+ Committed = MiIsEntireRangeCommitted(StartingAddress,
+ EndingAddress,
+ Vad,
+ Process);
+ if (!Committed)
+ {
+ /* Fail */
+ DPRINT1("The entire range is not committed\n");
+ Status = STATUS_NOT_COMMITTED;
+ MiUnlockProcessWorkingSetUnsafe(Process, Thread);
+ goto FailPath;
+ }
/* Compute starting and ending PTE and PDE addresses */
PointerPde = MiAddressToPde(StartingAddress);
if (PointerPte->u.Long != 0)
{
/* Capture the page protection and make the PDE valid */
- *OldAccessProtection = MiGetPageProtection(PointerPte);
+ OldProtect = MiGetPageProtection(PointerPte);
MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
}
else
{
/* Grab the old protection from the VAD itself */
- *OldAccessProtection = MmProtectToValue[Vad->u.VadFlags.Protection];
+ OldProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
}
/* Loop all the PTEs now */
while (PointerPte <= LastPte)
{
/* Check if we've crossed a PDE boundary and make the new PDE valid too */
- if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
+ if (MiIsPteOnPdeBoundary(PointerPte))
{
PointerPde = MiAddressToPte(PointerPte);
MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL);
}
- /* Capture the PTE and see what we're dealing with */
+ /* Capture the PTE and check if it was empty */
PteContents = *PointerPte;
if (PteContents.u.Long == 0)
{
/* This used to be a zero PTE and it no longer is, so we must add a
reference to the pagetable. */
- UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(MiPteToAddress(PointerPte))];
- (*UsedPageTableEntries)++;
- ASSERT((*UsedPageTableEntries) <= PTE_COUNT);
+ MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
}
- else if (PteContents.u.Hard.Valid == 1)
+
+ /* Check what kind of PTE we are dealing with */
+ if (PteContents.u.Hard.Valid == 1)
{
/* Get the PFN entry */
Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(&PteContents));
if ((NewAccessProtection & PAGE_NOACCESS) ||
(NewAccessProtection & PAGE_GUARD))
{
- /* TODO */
- UNIMPLEMENTED;
+ /* The page should be in the WS and we should make it transition now */
+ DPRINT1("Making valid page invalid is not yet supported!\n");
+ Status = STATUS_NOT_IMPLEMENTED;
+ /* Unlock the working set */
+ MiUnlockProcessWorkingSetUnsafe(Process, Thread);
+ goto FailPath;
}
/* Write the protection mask and write it with a TLB flush */
ASSERT(PteContents.u.Soft.Transition == 0);
/* The PTE is already demand-zero, just update the protection mask */
- PointerPte->u.Soft.Protection = ProtectionMask;
+ PteContents.u.Soft.Protection = ProtectionMask;
+ MI_WRITE_INVALID_PTE(PointerPte, PteContents);
+ ASSERT(PointerPte->u.Long != 0);
}
+ /* Move to the next PTE */
PointerPte++;
}
- /* Unlock the working set and update quota charges if needed, then return */
- //MiUnlockProcessWorkingSet(Thread, Process);
+ /* Unlock the working set */
+ MiUnlockProcessWorkingSetUnsafe(Process, Thread);
}
-FailPath:
/* Unlock the address space */
MmUnlockAddressSpace(AddressSpace);
- /* Return parameters */
- *NumberOfBytesToProtect = (SIZE_T)((PUCHAR)EndingAddress - (PUCHAR)StartingAddress + 1);
+ /* Return parameters and success */
+ *NumberOfBytesToProtect = EndingAddress - StartingAddress + 1;
*BaseAddress = (PVOID)StartingAddress;
+ *OldAccessProtection = OldProtect;
+ return STATUS_SUCCESS;
+FailPath:
+ /* Unlock the address space and return the failure code */
+ MmUnlockAddressSpace(AddressSpace);
return Status;
}
ULONG PteCount = 0;
PMMPFN Pfn1;
MMPTE PteContents;
- PUSHORT UsedPageTableEntries;
PETHREAD CurrentThread = PsGetCurrentThread();
//
PointerPde = MiAddressToPde(StartingAddress);
PointerPte = MiAddressToPte(StartingAddress);
if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT);
- MiLockWorkingSet(CurrentThread, &Process->Vm);
+ MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
//
// Make the PDE valid, and now loop through each page's worth of data
//
// Check if we've crossed a PDE boundary
//
- if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0)
+ if (MiIsPteOnPdeBoundary(PointerPte))
{
//
// Get the new PDE and flush the valid PTEs we had built up until
// This used to be a zero PTE and it no longer is, so we must add a
// reference to the pagetable.
//
- UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(StartingAddress)];
- (*UsedPageTableEntries)++;
- ASSERT((*UsedPageTableEntries) <= PTE_COUNT);
+ MiIncrementPageTableReferences(StartingAddress);
//
// Next, we account for decommitted PTEs and make the PTE as such
// release the working set and return the commit reduction accounting.
//
if (PteCount) MiProcessValidPteList(ValidPteList, PteCount);
- MiUnlockWorkingSet(CurrentThread, &Process->Vm);
+ MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
return CommitReduction;
}
return Status;
}
-NTSTATUS
-NTAPI
-NtLockVirtualMemory(IN HANDLE ProcessHandle,
- IN OUT PVOID *BaseAddress,
- IN OUT PSIZE_T NumberOfBytesToLock,
- IN ULONG MapType)
+FORCEINLINE
+BOOLEAN
+MI_IS_LOCKED_VA(
+ PMMPFN Pfn1,
+ ULONG LockType)
{
- PEPROCESS Process;
- PEPROCESS CurrentProcess = PsGetCurrentProcess();
- NTSTATUS Status;
- BOOLEAN Attached = FALSE;
- KAPC_STATE ApcState;
- KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
- PVOID CapturedBaseAddress;
- SIZE_T CapturedBytesToLock;
- PAGED_CODE();
+ // HACK until we have proper WSLIST support
+ PMMWSLE Wsle = &Pfn1->Wsle;
- //
- // Validate flags
- //
- if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
- {
- //
- // Invalid set of flags
- //
- return STATUS_INVALID_PARAMETER;
- }
+ if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
+ return TRUE;
+ if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
+ return TRUE;
- //
- // At least one flag must be specified
- //
- if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
+ return FALSE;
+}
+
+FORCEINLINE
+VOID
+MI_LOCK_VA(
+ PMMPFN Pfn1,
+ ULONG LockType)
+{
+ // HACK until we have proper WSLIST support
+ PMMWSLE Wsle = &Pfn1->Wsle;
+
+ if (!Wsle->u1.e1.LockedInWs &&
+ !Wsle->u1.e1.LockedInMemory)
{
- //
- // No flag given
- //
- return STATUS_INVALID_PARAMETER;
+ MiReferenceProbedPageAndBumpLockCount(Pfn1);
}
- //
+ if (LockType & MAP_PROCESS)
+ Wsle->u1.e1.LockedInWs = 1;
+ if (LockType & MAP_SYSTEM)
+ Wsle->u1.e1.LockedInMemory = 1;
+}
+
+FORCEINLINE
+VOID
+MI_UNLOCK_VA(
+ PMMPFN Pfn1,
+ ULONG LockType)
+{
+ // HACK until we have proper WSLIST support
+ PMMWSLE Wsle = &Pfn1->Wsle;
+
+ if (LockType & MAP_PROCESS)
+ Wsle->u1.e1.LockedInWs = 0;
+ if (LockType & MAP_SYSTEM)
+ Wsle->u1.e1.LockedInMemory = 0;
+
+ if (!Wsle->u1.e1.LockedInWs &&
+ !Wsle->u1.e1.LockedInMemory)
+ {
+ MiDereferencePfnAndDropLockCount(Pfn1);
+ }
+}
+
+static
+NTSTATUS
+MiCheckVadsForLockOperation(
+ _Inout_ PVOID *BaseAddress,
+ _Inout_ PSIZE_T RegionSize,
+ _Inout_ PVOID *EndAddress)
+
+{
+ PMMVAD Vad;
+ PVOID CurrentVa;
+
+ /* Get the base address and align the start address */
+ *EndAddress = (PUCHAR)*BaseAddress + *RegionSize;
+ *EndAddress = ALIGN_UP_POINTER_BY(*EndAddress, PAGE_SIZE);
+ *BaseAddress = ALIGN_DOWN_POINTER_BY(*BaseAddress, PAGE_SIZE);
+
+ /* First loop and check all VADs */
+ CurrentVa = *BaseAddress;
+ while (CurrentVa < *EndAddress)
+ {
+ /* Get VAD */
+ Vad = MiLocateAddress(CurrentVa);
+ if (Vad == NULL)
+ {
+ /// FIXME: this might be a memory area for a section view...
+ return STATUS_ACCESS_VIOLATION;
+ }
+
+ /* Check VAD type */
+ if ((Vad->u.VadFlags.VadType != VadNone) &&
+ (Vad->u.VadFlags.VadType != VadImageMap) &&
+ (Vad->u.VadFlags.VadType != VadWriteWatch))
+ {
+ *EndAddress = CurrentVa;
+ *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
+ return STATUS_INCOMPATIBLE_FILE_MAP;
+ }
+
+ CurrentVa = (PVOID)((Vad->EndingVpn + 1) << PAGE_SHIFT);
+ }
+
+ *RegionSize = (PUCHAR)*EndAddress - (PUCHAR)*BaseAddress;
+ return STATUS_SUCCESS;
+}
+
+static
+NTSTATUS
+MiLockVirtualMemory(
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG MapType)
+{
+ PEPROCESS CurrentProcess;
+ PMMSUPPORT AddressSpace;
+ PVOID CurrentVa, EndAddress;
+ PMMPTE PointerPte, LastPte;
+ PMMPDE PointerPde;
+#if (_MI_PAGING_LEVELS >= 3)
+ PMMPDE PointerPpe;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ PMMPDE PointerPxe;
+#endif
+ PMMPFN Pfn1;
+ NTSTATUS Status, TempStatus;
+
+ /* Lock the address space */
+ AddressSpace = MmGetCurrentAddressSpace();
+ MmLockAddressSpace(AddressSpace);
+
+ /* Make sure we still have an address space */
+ CurrentProcess = PsGetCurrentProcess();
+ if (CurrentProcess->VmDeleted)
+ {
+ Status = STATUS_PROCESS_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ /* Check the VADs in the requested range */
+ Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
+ if (!NT_SUCCESS(Status))
+ {
+ goto Cleanup;
+ }
+
+ /* Enter SEH for probing */
+ _SEH2_TRY
+ {
+ /* Loop all pages and probe them */
+ CurrentVa = *BaseAddress;
+ while (CurrentVa < EndAddress)
+ {
+ (void)(*(volatile CHAR*)CurrentVa);
+ CurrentVa = (PUCHAR)CurrentVa + PAGE_SIZE;
+ }
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ goto Cleanup;
+ }
+ _SEH2_END;
+
+ /* All pages were accessible, since we hold the address space lock, nothing
+ can be de-committed. Assume success for now. */
+ Status = STATUS_SUCCESS;
+
+ /* Get the PTE and PDE */
+ PointerPte = MiAddressToPte(*BaseAddress);
+ PointerPde = MiAddressToPde(*BaseAddress);
+#if (_MI_PAGING_LEVELS >= 3)
+ PointerPpe = MiAddressToPpe(*BaseAddress);
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ PointerPxe = MiAddressToPxe(*BaseAddress);
+#endif
+
+ /* Get the last PTE */
+ LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
+
+ /* Lock the process working set */
+ MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+ /* Loop the pages */
+ do
+ {
+ /* Check for a page that is not accessible */
+ while (
+#if (_MI_PAGING_LEVELS == 4)
+ (PointerPxe->u.Hard.Valid == 0) ||
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+ (PointerPpe->u.Hard.Valid == 0) ||
+#endif
+ (PointerPde->u.Hard.Valid == 0) ||
+ (PointerPte->u.Hard.Valid == 0))
+ {
+ /* Release process working set */
+ MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+ /* Access the page */
+ CurrentVa = MiPteToAddress(PointerPte);
+
+ //HACK: Pass a placeholder TrapInformation so the fault handler knows we're unlocked
+ TempStatus = MmAccessFault(TRUE, CurrentVa, KernelMode, (PVOID)0xBADBADA3);
+ if (!NT_SUCCESS(TempStatus))
+ {
+ // This should only happen, when remote backing storage is not accessible
+ ASSERT(FALSE);
+ Status = TempStatus;
+ goto Cleanup;
+ }
+
+ /* Lock the process working set */
+ MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+ }
+
+ /* Get the PFN */
+ Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+ ASSERT(Pfn1 != NULL);
+
+ /* Check the previous lock status */
+ if (MI_IS_LOCKED_VA(Pfn1, MapType))
+ {
+ Status = STATUS_WAS_LOCKED;
+ }
+
+ /* Lock it */
+ MI_LOCK_VA(Pfn1, MapType);
+
+ /* Go to the next PTE */
+ PointerPte++;
+
+ /* Check if we're on a PDE boundary */
+ if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
+#if (_MI_PAGING_LEVELS >= 3)
+ if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
+#endif
+ } while (PointerPte <= LastPte);
+
+ /* Release process working set */
+ MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+Cleanup:
+ /* Unlock address space */
+ MmUnlockAddressSpace(AddressSpace);
+
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+NtLockVirtualMemory(IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T NumberOfBytesToLock,
+ IN ULONG MapType)
+{
+ PEPROCESS Process;
+ PEPROCESS CurrentProcess = PsGetCurrentProcess();
+ NTSTATUS Status;
+ BOOLEAN Attached = FALSE;
+ KAPC_STATE ApcState;
+ KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
+ PVOID CapturedBaseAddress;
+ SIZE_T CapturedBytesToLock;
+ PAGED_CODE();
+
+ //
+ // Validate flags
+ //
+ if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM)))
+ {
+ //
+ // Invalid set of flags
+ //
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // At least one flag must be specified
+ //
+ if (!(MapType & (MAP_PROCESS | MAP_SYSTEM)))
+ {
+ //
+ // No flag given
+ //
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
// Enter SEH for probing
//
_SEH2_TRY
}
//
- // Oops :(
+ // Call the internal function
//
- UNIMPLEMENTED;
+ Status = MiLockVirtualMemory(&CapturedBaseAddress,
+ &CapturedBytesToLock,
+ MapType);
//
// Detach if needed
// Return data to user
//
*BaseAddress = CapturedBaseAddress;
- *NumberOfBytesToLock = 0;
+ *NumberOfBytesToLock = CapturedBytesToLock;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
//
// Return status
//
- return STATUS_SUCCESS;
+ return Status;
+}
+
+
+static
+NTSTATUS
+MiUnlockVirtualMemory(
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG MapType)
+{
+ PEPROCESS CurrentProcess;
+ PMMSUPPORT AddressSpace;
+ PVOID EndAddress;
+ PMMPTE PointerPte, LastPte;
+ PMMPDE PointerPde;
+#if (_MI_PAGING_LEVELS >= 3)
+ PMMPDE PointerPpe;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ PMMPDE PointerPxe;
+#endif
+ PMMPFN Pfn1;
+ NTSTATUS Status;
+
+ /* Lock the address space */
+ AddressSpace = MmGetCurrentAddressSpace();
+ MmLockAddressSpace(AddressSpace);
+
+ /* Make sure we still have an address space */
+ CurrentProcess = PsGetCurrentProcess();
+ if (CurrentProcess->VmDeleted)
+ {
+ Status = STATUS_PROCESS_IS_TERMINATING;
+ goto Cleanup;
+ }
+
+ /* Check the VADs in the requested range */
+ Status = MiCheckVadsForLockOperation(BaseAddress, RegionSize, &EndAddress);
+
+ /* Note: only bail out, if we hit an area without a VAD. If we hit an
+ incompatible VAD we continue, like Windows does */
+ if (Status == STATUS_ACCESS_VIOLATION)
+ {
+ Status = STATUS_NOT_LOCKED;
+ goto Cleanup;
+ }
+
+ /* Get the PTE and PDE */
+ PointerPte = MiAddressToPte(*BaseAddress);
+ PointerPde = MiAddressToPde(*BaseAddress);
+#if (_MI_PAGING_LEVELS >= 3)
+ PointerPpe = MiAddressToPpe(*BaseAddress);
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ PointerPxe = MiAddressToPxe(*BaseAddress);
+#endif
+
+ /* Get the last PTE */
+ LastPte = MiAddressToPte((PVOID)((ULONG_PTR)EndAddress - 1));
+
+ /* Lock the process working set */
+ MiLockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+ /* Loop the pages */
+ do
+ {
+ /* Check for a page that is not present */
+ if (
+#if (_MI_PAGING_LEVELS == 4)
+ (PointerPxe->u.Hard.Valid == 0) ||
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+ (PointerPpe->u.Hard.Valid == 0) ||
+#endif
+ (PointerPde->u.Hard.Valid == 0) ||
+ (PointerPte->u.Hard.Valid == 0))
+ {
+ /* Remember it, but keep going */
+ Status = STATUS_NOT_LOCKED;
+ }
+ else
+ {
+ /* Get the PFN */
+ Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+ ASSERT(Pfn1 != NULL);
+
+ /* Check if all of the requested locks are present */
+ if (((MapType & MAP_SYSTEM) && !MI_IS_LOCKED_VA(Pfn1, MAP_SYSTEM)) ||
+ ((MapType & MAP_PROCESS) && !MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS)))
+ {
+ /* Remember it, but keep going */
+ Status = STATUS_NOT_LOCKED;
+
+ /* Check if no lock is present */
+ if (!MI_IS_LOCKED_VA(Pfn1, MAP_PROCESS | MAP_SYSTEM))
+ {
+ DPRINT1("FIXME: Should remove the page from WS\n");
+ }
+ }
+ }
+
+ /* Go to the next PTE */
+ PointerPte++;
+
+ /* Check if we're on a PDE boundary */
+ if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
+#if (_MI_PAGING_LEVELS >= 3)
+ if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
+#endif
+ } while (PointerPte <= LastPte);
+
+ /* Check if we hit a page that was not locked */
+ if (Status == STATUS_NOT_LOCKED)
+ {
+ goto CleanupWithWsLock;
+ }
+
+ /* All pages in the region were locked, so unlock them all */
+
+ /* Get the PTE and PDE */
+ PointerPte = MiAddressToPte(*BaseAddress);
+ PointerPde = MiAddressToPde(*BaseAddress);
+#if (_MI_PAGING_LEVELS >= 3)
+ PointerPpe = MiAddressToPpe(*BaseAddress);
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ PointerPxe = MiAddressToPxe(*BaseAddress);
+#endif
+
+ /* Loop the pages */
+ do
+ {
+ /* Unlock it */
+ Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
+ MI_UNLOCK_VA(Pfn1, MapType);
+
+ /* Go to the next PTE */
+ PointerPte++;
+
+ /* Check if we're on a PDE boundary */
+ if (MiIsPteOnPdeBoundary(PointerPte)) PointerPde++;
+#if (_MI_PAGING_LEVELS >= 3)
+ if (MiIsPteOnPpeBoundary(PointerPte)) PointerPpe++;
+#endif
+#if (_MI_PAGING_LEVELS == 4)
+ if (MiIsPteOnPxeBoundary(PointerPte)) PointerPxe++;
+#endif
+ } while (PointerPte <= LastPte);
+
+ /* Everything is done */
+ Status = STATUS_SUCCESS;
+
+CleanupWithWsLock:
+
+ /* Release process working set */
+ MiUnlockProcessWorkingSet(CurrentProcess, PsGetCurrentThread());
+
+Cleanup:
+ /* Unlock address space */
+ MmUnlockAddressSpace(AddressSpace);
+
+ return Status;
}
+
NTSTATUS
NTAPI
NtUnlockVirtualMemory(IN HANDLE ProcessHandle,
}
//
- // Oops :(
+ // Call the internal function
//
- UNIMPLEMENTED;
+ Status = MiUnlockVirtualMemory(&CapturedBaseAddress,
+ &CapturedBytesToUnlock,
+ MapType);
//
// Detach if needed
//
// Return data to user
//
- *BaseAddress = PAGE_ALIGN(CapturedBaseAddress);
- *NumberOfBytesToUnlock = 0;
+ *BaseAddress = CapturedBaseAddress;
+ *NumberOfBytesToUnlock = CapturedBytesToUnlock;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
//
// Catch illegal base address
//
- if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2;
+ if (BaseAddress > MM_HIGHEST_USER_ADDRESS) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_2);
//
// Catch illegal region size
//
// Fail
//
- return STATUS_INVALID_PARAMETER_3;
+ _SEH2_YIELD(return STATUS_INVALID_PARAMETER_3);
}
//
//
// Must have a count
//
- if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5;
+ if (CapturedEntryCount == 0) _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
//
// Can't be larger than the maximum
//
// Fail
//
- return STATUS_INVALID_PARAMETER_5;
+ _SEH2_YIELD(return STATUS_INVALID_PARAMETER_5);
}
//
PEPROCESS Process;
PMEMORY_AREA MemoryArea;
PFN_NUMBER PageCount;
- PMMVAD Vad, FoundVad;
- PUSHORT UsedPageTableEntries;
+ PMMVAD Vad = NULL, FoundVad;
NTSTATUS Status;
PMMSUPPORT AddressSpace;
PVOID PBaseAddress;
- ULONG_PTR PRegionSize, StartingAddress, EndingAddress;
+ ULONG_PTR PRegionSize, StartingAddress, EndingAddress, HighestAddress;
PEPROCESS CurrentProcess = PsGetCurrentProcess();
KPROCESSOR_MODE PreviousMode = KeGetPreviousMode();
PETHREAD CurrentThread = PsGetCurrentThread();
BOOLEAN Attached = FALSE, ChangeProtection = FALSE;
MMPTE TempPte;
PMMPTE PointerPte, PointerPde, LastPte;
+ TABLE_SEARCH_RESULT Result;
+ PMMADDRESS_NODE Parent;
PAGED_CODE();
/* Check for valid Zero bits */
- if (ZeroBits > 21)
+ if (ZeroBits > MI_MAX_ZERO_BITS)
{
DPRINT1("Too many zero bits\n");
return STATUS_INVALID_PARAMETER_3;
/* Check for valid Allocation Types */
if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL |
- MEM_TOP_DOWN | MEM_WRITE_WATCH)))
+ MEM_TOP_DOWN | MEM_WRITE_WATCH | MEM_LARGE_PAGES)))
{
DPRINT1("Invalid Allocation Type\n");
return STATUS_INVALID_PARAMETER_5;
return STATUS_INVALID_PARAMETER_5;
}
- /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
- if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE))
- {
- DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n");
- return STATUS_INVALID_PARAMETER_5;
- }
-
/* Check for valid MEM_PHYSICAL usage */
if (AllocationType & MEM_PHYSICAL)
{
+ /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */
+ if (!(AllocationType & MEM_RESERVE))
+ {
+ DPRINT1("MEM_PHYSICAL used without MEM_RESERVE\n");
+ return STATUS_INVALID_PARAMETER_5;
+ }
+
/* Only these flags are allowed with MEM_PHYSIAL */
if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL))
{
}
}
- //
- // Force PAGE_READWRITE for everything, for now
- //
- Protect = PAGE_READWRITE;
-
/* Calculate the protection mask and make sure it's valid */
ProtectionMask = MiMakeProtectionMask(Protect);
if (ProtectionMask == MM_INVALID_PROTECTION)
{
/* Make sure they are writable */
ProbeForWritePointer(UBaseAddress);
- ProbeForWriteUlong(URegionSize);
+ ProbeForWriteSize_t(URegionSize);
}
/* Capture their values */
_SEH2_END;
/* Make sure the allocation isn't past the VAD area */
- if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS)
+ if (PBaseAddress > MM_HIGHEST_VAD_ADDRESS)
{
DPRINT1("Virtual allocation base above User Space\n");
return STATUS_INVALID_PARAMETER_2;
}
//
- // Assert on the things we don't yet support
+ // Fail on the things we don't yet support
//
- ASSERT(ZeroBits == 0);
- ASSERT((AllocationType & MEM_LARGE_PAGES) == 0);
- ASSERT((AllocationType & MEM_PHYSICAL) == 0);
- ASSERT((AllocationType & MEM_WRITE_WATCH) == 0);
- ASSERT((AllocationType & MEM_TOP_DOWN) == 0);
- ASSERT((AllocationType & MEM_RESET) == 0);
- ASSERT(Process->VmTopDown == 0);
+ if ((AllocationType & MEM_LARGE_PAGES) == MEM_LARGE_PAGES)
+ {
+ DPRINT1("MEM_LARGE_PAGES not supported\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto FailPathNoLock;
+ }
+ if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
+ {
+ DPRINT1("MEM_PHYSICAL not supported\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto FailPathNoLock;
+ }
+ if ((AllocationType & MEM_WRITE_WATCH) == MEM_WRITE_WATCH)
+ {
+ DPRINT1("MEM_WRITE_WATCH not supported\n");
+ Status = STATUS_INVALID_PARAMETER;
+ goto FailPathNoLock;
+ }
//
// Check if the caller is reserving memory, or committing memory and letting
//
// Do not allow COPY_ON_WRITE through this API
//
- if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY))
+ if (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
{
DPRINT1("Copy on write not allowed through this path\n");
Status = STATUS_INVALID_PAGE_PROTECTION;
PageCount = BYTES_TO_PAGES(PRegionSize);
EndingAddress = 0;
StartingAddress = 0;
+
+ //
+ // Check if ZeroBits were specified
+ //
+ if (ZeroBits != 0)
+ {
+ //
+ // Calculate the highest address and check if it's valid
+ //
+ HighestAddress = MAXULONG_PTR >> ZeroBits;
+ if (HighestAddress > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS)
+ {
+ Status = STATUS_INVALID_PARAMETER_3;
+ goto FailPathNoLock;
+ }
+ }
+ else
+ {
+ //
+ // Use the highest VAD address as maximum
+ //
+ HighestAddress = (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS;
+ }
}
else
{
// expected 64KB granularity, and see where the ending address will
// fall based on the aligned address and the passed in region size
//
- StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
+ StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K);
PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress);
}
// Allocate and initialize the VAD
//
Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV');
- ASSERT(Vad != NULL);
+ if (Vad == NULL)
+ {
+ DPRINT1("Failed to allocate a VAD!\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto FailPathNoLock;
+ }
+
Vad->u.LongFlags = 0;
if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1;
Vad->u.VadFlags.Protection = ProtectionMask;
//
if (!PBaseAddress)
{
- Status = MiFindEmptyAddressRangeInTree(PRegionSize,
- _64K,
- &Process->VadRoot,
- (PMMADDRESS_NODE*)&Process->VadFreeHint,
- &StartingAddress);
- if (!NT_SUCCESS(Status)) goto FailPath;
+ /* Which way should we search? */
+ if ((AllocationType & MEM_TOP_DOWN) || Process->VmTopDown)
+ {
+ /* Find an address top-down */
+ Result = MiFindEmptyAddressRangeDownTree(PRegionSize,
+ HighestAddress,
+ _64K,
+ &Process->VadRoot,
+ &StartingAddress,
+ &Parent);
+ }
+ else
+ {
+ /* Find an address bottom-up */
+ Result = MiFindEmptyAddressRangeInTree(PRegionSize,
+ _64K,
+ &Process->VadRoot,
+ &Parent,
+ &StartingAddress);
+ }
+
+ if (Result == TableFoundNode)
+ {
+ Status = STATUS_NO_MEMORY;
+ goto FailPath;
+ }
//
// Now we know where the allocation ends. Make sure it doesn't end up
// somewhere in kernel mode.
//
- EndingAddress = ((ULONG_PTR)StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
- if ((PVOID)EndingAddress > MM_HIGHEST_VAD_ADDRESS)
+ ASSERT(StartingAddress != 0);
+ ASSERT(StartingAddress < (ULONG_PTR)MM_HIGHEST_USER_ADDRESS);
+ EndingAddress = (StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1);
+ ASSERT(EndingAddress > StartingAddress);
+ if (EndingAddress > HighestAddress)
{
Status = STATUS_NO_MEMORY;
goto FailPath;
}
}
- else if (MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
- EndingAddress >> PAGE_SHIFT,
- &Process->VadRoot))
+ else
{
- //
- // The address specified is in conflict!
- //
- Status = STATUS_CONFLICTING_ADDRESSES;
- goto FailPath;
+ /* Make sure it doesn't conflict with an existing allocation */
+ Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
+ EndingAddress >> PAGE_SHIFT,
+ &Process->VadRoot,
+ &Parent);
+ if (Result == TableFoundNode)
+ {
+ //
+ // The address specified is in conflict!
+ //
+ Status = STATUS_CONFLICTING_ADDRESSES;
+ goto FailPath;
+ }
}
//
// Write out the VAD fields for this allocation
//
- Vad->StartingVpn = (ULONG_PTR)StartingAddress >> PAGE_SHIFT;
- Vad->EndingVpn = (ULONG_PTR)EndingAddress >> PAGE_SHIFT;
+ Vad->StartingVpn = StartingAddress >> PAGE_SHIFT;
+ Vad->EndingVpn = EndingAddress >> PAGE_SHIFT;
//
// FIXME: Should setup VAD bitmap
//
// Lock the working set and insert the VAD into the process VAD tree
//
- MiLockProcessWorkingSet(Process, CurrentThread);
+ MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
Vad->ControlArea = NULL; // For Memory-Area hack
- MiInsertVad(Vad, Process);
- MiUnlockProcessWorkingSet(Process, CurrentThread);
+ Process->VadRoot.NodeHint = Vad;
+ MiInsertNode(&Process->VadRoot, (PVOID)Vad, Parent, Result);
+ MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
+
+ //
+ // Make sure the actual region size is at least as big as the
+ // requested region size and update the value
+ //
+ ASSERT(PRegionSize <= (EndingAddress + 1 - StartingAddress));
+ PRegionSize = (EndingAddress + 1 - StartingAddress);
//
// Update the virtual size of the process, and if this is now the highest
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
+ //
+ // Ignore exception!
+ //
}
_SEH2_END;
return STATUS_SUCCESS;
// on the user input, and then compute the actual region size once all the
// alignments have been done.
//
- StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1));
+ StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress);
PRegionSize = EndingAddress - StartingAddress + 1;
//
//
// Get the VAD for this address range, and make sure it exists
//
- FoundVad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
- EndingAddress >> PAGE_SHIFT,
- &Process->VadRoot);
- if (!FoundVad)
+ Result = MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT,
+ EndingAddress >> PAGE_SHIFT,
+ &Process->VadRoot,
+ (PMMADDRESS_NODE*)&FoundVad);
+ if (Result != TableFoundNode)
{
DPRINT1("Could not find a VAD for this allocation\n");
Status = STATUS_CONFLICTING_ADDRESSES;
goto FailPath;
}
+ if ((AllocationType & MEM_RESET) == MEM_RESET)
+ {
+ /// @todo HACK: pretend success
+ DPRINT("MEM_RESET not supported\n");
+ Status = STATUS_SUCCESS;
+ goto FailPath;
+ }
+
//
// These kinds of VADs are illegal for this Windows function when trying to
// commit an existing range
//
// Make sure that this address range actually fits within the VAD for it
//
- if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) &&
+ if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) ||
((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn))
{
DPRINT1("Address range does not fit into the VAD\n");
}
//
- // If this is an existing section view, we call the old RosMm routine which
- // has the relevant code required to handle the section scenario. In the future
- // we will limit this even more so that there's almost nothing that the code
- // needs to do, and it will become part of section.c in RosMm
+ // Make sure this is an ARM3 section
//
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
{
- return MiRosAllocateVirtualMemory(ProcessHandle,
- Process,
- MemoryArea,
- AddressSpace,
- UBaseAddress,
- Attached,
- URegionSize,
- AllocationType,
- Protect);
+ DPRINT1("Illegal commit of non-ARM3 section!\n");
+ Status = STATUS_ALREADY_COMMITTED;
+ goto FailPath;
}
// Is this a previously reserved section being committed? If so, enter the
}
//
- // We should make sure that the section's permissions aren't being messed with
+ // We should make sure that the section's permissions aren't being
+ // messed with
//
if (FoundVad->u.VadFlags.NoChange)
{
- DPRINT1("SEC_NO_CHANGE section being touched. Assuming this is ok\n");
+ //
+ // Make sure it's okay to touch it
+ // Note: The Windows 2003 kernel has a bug here, passing the
+ // unaligned base address together with the aligned size,
+ // potentially covering a region larger than the actual allocation.
+ // Might be exposed through NtGdiCreateDIBSection w/ section handle
+ // For now we keep this behavior.
+ // TODO: analyze possible implications, create test case
+ //
+ Status = MiCheckSecuredVad(FoundVad,
+ PBaseAddress,
+ PRegionSize,
+ ProtectionMask);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Secured VAD being messed around with\n");
+ goto FailPath;
+ }
}
//
//
TempPte.u.Long = 0;
TempPte.u.Soft.Protection = ProtectionMask;
+ NT_ASSERT(TempPte.u.Long != 0);
//
// Get the PTE, PDE and the last PTE for this address range
//
// Lock the working set while we play with user pages and page tables
//
- //MiLockWorkingSet(CurrentThread, AddressSpace);
+ MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
//
// Make the current page table valid, and then loop each page within it
//
// Have we crossed into a new page table?
//
- if (!(((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)))
+ if (MiIsPteOnPdeBoundary(PointerPte))
{
//
// Get the PDE and now make it valid too
// First increment the count of pages in the page table for this
// process
//
- UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(MiPteToAddress(PointerPte))];
- (*UsedPageTableEntries)++;
- ASSERT((*UsedPageTableEntries) <= PTE_COUNT);
+ MiIncrementPageTableReferences(MiPteToAddress(PointerPte));
//
// And now write the invalid demand-zero PTE as requested
// There's a change in protection, remember this for later, but do
// not yet handle it.
//
- DPRINT1("Protection change to: 0x%lx not implemented\n", Protect);
ChangeProtection = TRUE;
}
PointerPte++;
}
- //
- // This path is not yet handled
- //
- ASSERT(ChangeProtection == FALSE);
-
//
// Release the working set lock, unlock the address space, and detach from
// the target process if it was not the current process. Also dereference the
// target process if this wasn't the case.
//
- //MiUnlockProcessWorkingSet(Process, CurrentThread);
+ MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
Status = STATUS_SUCCESS;
FailPath:
MmUnlockAddressSpace(AddressSpace);
+
+ if (!NT_SUCCESS(Status))
+ {
+ if (Vad != NULL)
+ {
+ ExFreePoolWithTag(Vad, 'SdaV');
+ }
+ }
+
+ //
+ // Check if we need to update the protection
+ //
+ if (ChangeProtection)
+ {
+ PVOID ProtectBaseAddress = (PVOID)StartingAddress;
+ SIZE_T ProtectSize = PRegionSize;
+ ULONG OldProtection;
+
+ //
+ // Change the protection of the region
+ //
+ MiProtectVirtualMemory(Process,
+ &ProtectBaseAddress,
+ &ProtectSize,
+ Protect,
+ &OldProtection);
+ }
+
FailPathNoLock:
if (Attached) KeUnstackDetachProcess(&ApcState);
if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process);
//
- // Use SEH to write back the base address and the region size. In the case
- // of an exception, we strangely do return back the exception code, even
- // though the memory *has* been allocated. This mimics Windows behavior and
- // there is not much we can do about it.
+ // Only write back results on success
//
- _SEH2_TRY
- {
- *URegionSize = PRegionSize;
- *UBaseAddress = (PVOID)StartingAddress;
- }
- _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ if (NT_SUCCESS(Status))
{
- Status = _SEH2_GetExceptionCode();
+ //
+ // Use SEH to write back the base address and the region size. In the case
+ // of an exception, we strangely do return back the exception code, even
+ // though the memory *has* been allocated. This mimics Windows behavior and
+ // there is not much we can do about it.
+ //
+ _SEH2_TRY
+ {
+ *URegionSize = PRegionSize;
+ *UBaseAddress = (PVOID)StartingAddress;
+ }
+ _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+ {
+ Status = _SEH2_GetExceptionCode();
+ }
+ _SEH2_END;
}
- _SEH2_END;
+
return Status;
}
PMEMORY_AREA MemoryArea;
SIZE_T PRegionSize;
PVOID PBaseAddress;
- ULONG_PTR CommitReduction = 0;
+ LONG_PTR CommitReduction = 0;
ULONG_PTR StartingAddress, EndingAddress;
PMMVAD Vad;
NTSTATUS Status;
}
//
- // These ASSERTs are here because ReactOS ARM3 does not currently implement
- // any other kinds of VADs.
+ // Only private memory (except rotate VADs) can be freed through here */
+ //
+ if ((!(Vad->u.VadFlags.PrivateMemory) &&
+ (Vad->u.VadFlags.VadType != VadRotatePhysical)) ||
+ (Vad->u.VadFlags.VadType == VadDevicePhysicalMemory))
+ {
+ DPRINT1("Attempt to free section memory\n");
+ Status = STATUS_UNABLE_TO_DELETE_SECTION;
+ goto FailPath;
+ }
+
+ //
+ // ARM3 does not yet handle protected VM
//
- ASSERT(Vad->u.VadFlags.PrivateMemory == 1);
ASSERT(Vad->u.VadFlags.NoChange == 0);
- ASSERT(Vad->u.VadFlags.VadType == VadNone);
//
// Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation
//
if (FreeType & MEM_RELEASE)
{
+ //
+ // ARM3 only supports this VAD in this path
+ //
+ ASSERT(Vad->u.VadFlags.VadType == VadNone);
+
//
// Is the caller trying to remove the whole VAD, or remove only a portion
// of it? If no region size is specified, then the assumption is that the
//
// Finally lock the working set and remove the VAD from the VAD tree
//
- MiLockWorkingSet(CurrentThread, AddressSpace);
+ MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
}
// the code path above when the caller sets a zero region size
// and the whole VAD is destroyed
//
- MiLockWorkingSet(CurrentThread, AddressSpace);
+ MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
ASSERT(Process->VadRoot.NumberGenericTableElements >= 1);
MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot);
}
// and then change the ending address of the VAD to be a bit
// smaller.
//
- MiLockWorkingSet(CurrentThread, AddressSpace);
+ MiLockProcessWorkingSetUnsafe(Process, CurrentThread);
CommitReduction = MiCalculatePageCommitment(StartingAddress,
EndingAddress,
Vad,
// around with process pages.
//
MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL);
- MiUnlockWorkingSet(CurrentThread, AddressSpace);
+ MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
Status = STATUS_SUCCESS;
FinalPath:
return Status;
}
+
+PHYSICAL_ADDRESS
+NTAPI
+MmGetPhysicalAddress(PVOID Address)
+{
+ PHYSICAL_ADDRESS PhysicalAddress;
+ MMPDE TempPde;
+ MMPTE TempPte;
+
+ /* Check if the PXE/PPE/PDE is valid */
+ if (
+#if (_MI_PAGING_LEVELS == 4)
+ (MiAddressToPxe(Address)->u.Hard.Valid) &&
+#endif
+#if (_MI_PAGING_LEVELS >= 3)
+ (MiAddressToPpe(Address)->u.Hard.Valid) &&
+#endif
+ (MiAddressToPde(Address)->u.Hard.Valid))
+ {
+ /* Check for large pages */
+ TempPde = *MiAddressToPde(Address);
+ if (TempPde.u.Hard.LargePage)
+ {
+ /* Physical address is base page + large page offset */
+ PhysicalAddress.QuadPart = (ULONG64)TempPde.u.Hard.PageFrameNumber << PAGE_SHIFT;
+ PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE * PTE_PER_PAGE - 1));
+ return PhysicalAddress;
+ }
+
+ /* Check if the PTE is valid */
+ TempPte = *MiAddressToPte(Address);
+ if (TempPte.u.Hard.Valid)
+ {
+ /* Physical address is base page + page offset */
+ PhysicalAddress.QuadPart = (ULONG64)TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
+ PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
+ return PhysicalAddress;
+ }
+ }
+
+ KeRosDumpStackFrames(NULL, 20);
+ DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
+ PhysicalAddress.QuadPart = 0;
+ return PhysicalAddress;
+}
+
+
/* EOF */