*/
/* INCLUDES *******************************************************************/
+/* So long, and Thanks for All the Fish */
#include <ntoskrnl.h>
#define NDEBUG
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
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
{
ObDereferenceObject(TargetProcess);
}
- /* Return the data, NtQueryInformation already probed it*/
+ /* Return the data, NtQueryInformation already probed it */
if (PreviousMode != KernelMode)
{
_SEH2_TRY
return Status;
}
-ULONG
+BOOLEAN
NTAPI
MiIsEntireRangeCommitted(IN ULONG_PTR StartingAddress,
IN ULONG_PTR EndingAddress,
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, OldProtect;
BOOLEAN Committed;
NTSTATUS Status = STATUS_SUCCESS;
+ PETHREAD Thread = PsGetCurrentThread();
- /* 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);
goto FailPath;
}
- /* Check for ROS specific memory area */
- MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress);
- if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW))
- {
- /* Empirical evidence suggests this is only used in one critical scenario and is always a no-op */
- OldProtect = NewAccessProtection;
- goto RosReturn;
- }
-
/* Is this section, or private memory? */
if (Vad->u.VadFlags.PrivateMemory == 0)
{
goto FailPath;
}
- //MiLockProcessWorkingSet(Thread, Process);
+ /* Lock the working set */
+ MiLockProcessWorkingSetUnsafe(Process, Thread);
/* Check if all pages in this range are committed */
Committed = MiIsEntireRangeCommitted(StartingAddress,
/* Fail */
DPRINT1("The entire range is not committed\n");
Status = STATUS_NOT_COMMITTED;
- //MiUnlockProcessWorkingSet(Thread, Process);
+ MiUnlockProcessWorkingSetUnsafe(Process, Thread);
goto FailPath;
}
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);
- DPRINT1("HACK: Not increasing UsedPageTableEntries count!\n");
+ 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));
(NewAccessProtection & PAGE_GUARD))
{
/* The page should be in the WS and we should make it transition now */
- UNIMPLEMENTED;
- //continue;
+ 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);
}
}
/* Unlock the working set */
- //MiUnlockProcessWorkingSet(Thread, Process);
+ MiUnlockProcessWorkingSetUnsafe(Process, Thread);
}
-RosReturn:
+
/* Unlock the address space */
MmUnlockAddressSpace(AddressSpace);
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;
}
+FORCEINLINE
+BOOLEAN
+MI_IS_LOCKED_VA(
+ PMMPFN Pfn1,
+ ULONG LockType)
+{
+ // HACK until we have proper WSLIST support
+ PMMWSLE Wsle = &Pfn1->Wsle;
+
+ if ((LockType & MAP_PROCESS) && (Wsle->u1.e1.LockedInWs))
+ return TRUE;
+ if ((LockType & MAP_SYSTEM) && (Wsle->u1.e1.LockedInMemory))
+ return TRUE;
+
+ 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)
+ {
+ 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,
}
//
- // 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);
}
//
PMEMORY_AREA MemoryArea;
PFN_NUMBER PageCount;
PMMVAD Vad, FoundVad;
- PUSHORT UsedPageTableEntries;
NTSTATUS Status;
PMMSUPPORT AddressSpace;
PVOID PBaseAddress;
}
}
- //
- // 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)
Status = STATUS_INVALID_PARAMETER;
goto FailPathNoLock;
}
- if ((AllocationType & MEM_PHYSICAL) ==MEM_PHYSICAL)
+ if ((AllocationType & MEM_PHYSICAL) == MEM_PHYSICAL)
{
DPRINT1("MEM_PHYSICAL not supported\n");
Status = STATUS_INVALID_PARAMETER;
if ((AllocationType & MEM_TOP_DOWN) == MEM_TOP_DOWN)
{
DPRINT1("MEM_TOP_DOWN not supported\n");
- Status = STATUS_INVALID_PARAMETER;
- goto FailPathNoLock;
- }
- if ((AllocationType & MEM_RESET) == MEM_RESET)
- {
- DPRINT1("MEM_RESET not supported\n");
- Status = STATUS_INVALID_PARAMETER;
- goto FailPathNoLock;
+ AllocationType &= ~MEM_TOP_DOWN;
}
+
if (Process->VmTopDown == 1)
{
DPRINT1("VmTopDown not supported\n");
//
// 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);
+ MiUnlockProcessWorkingSetUnsafe(Process, CurrentThread);
//
// Update the virtual size of the process, and if this is now the highest
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");
// Make sure this is an ARM3 section
//
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress));
- ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3);
+ if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3)
+ {
+ 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
// special section path
}
//
- // 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
+ //
+ 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);
+
+ //
+ // 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);
PMEMORY_AREA MemoryArea;
SIZE_T PRegionSize;
PVOID PBaseAddress;
- ULONG_PTR CommitReduction = 0;
+ LONG_PTR CommitReduction = 0;
ULONG_PTR StartingAddress, EndingAddress;
PMMVAD Vad;
NTSTATUS Status;
//
// 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 = 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 = TempPte.u.Hard.PageFrameNumber << PAGE_SHIFT;
+ PhysicalAddress.QuadPart += ((ULONG_PTR)Address & (PAGE_SIZE - 1));
+ return PhysicalAddress;
+ }
+ }
+
+ DPRINT1("MM:MmGetPhysicalAddressFailed base address was %p\n", Address);
+ PhysicalAddress.QuadPart = 0;
+ return PhysicalAddress;
+}
+
+
/* EOF */