/* PRIVATE FUNCTIONS **********************************************************/
+ULONG
+NTAPI
+MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress,
+ IN PEPROCESS CurrentProcess)
+{
+ NTSTATUS Status;
+ BOOLEAN LockChange = FALSE;
+
+ /* Must be a non-pool page table, since those are double-mapped already */
+ ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS);
+ ASSERT((PageTableVirtualAddress < MmPagedPoolStart) ||
+ (PageTableVirtualAddress > MmPagedPoolEnd));
+
+ /* Working set lock or PFN lock should be held */
+ ASSERT(KeAreAllApcsDisabled() == TRUE);
+
+ /* Check if the page table is valid */
+ while (!MmIsAddressValid(PageTableVirtualAddress))
+ {
+ /* Fault it in */
+ Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This should not fail */
+ KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
+ 1,
+ Status,
+ (ULONG_PTR)CurrentProcess,
+ (ULONG_PTR)PageTableVirtualAddress);
+ }
+
+ /* This flag will be useful later when we do better locking */
+ LockChange = TRUE;
+ }
+
+ /* Let caller know what the lock state is */
+ return LockChange;
+}
+
+ULONG
+NTAPI
+MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress,
+ IN KIRQL OldIrql)
+{
+ NTSTATUS Status;
+ BOOLEAN LockChange = FALSE;
+
+ /* Must be e kernel address */
+ ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS);
+
+ /* Check if the page is valid */
+ while (!MmIsAddressValid(VirtualAddress))
+ {
+ /* Release the PFN database */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+
+ /* Fault it in */
+ Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This should not fail */
+ KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR,
+ 3,
+ Status,
+ 0,
+ (ULONG_PTR)VirtualAddress);
+ }
+
+ /* This flag will be useful later when we do better locking */
+ LockChange = TRUE;
+
+ /* Lock the PFN database */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ }
+
+ /* Let caller know what the lock state is */
+ return LockChange;
+}
+
PFN_NUMBER
NTAPI
MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
return ActualPages;
}
+VOID
+NTAPI
+MiDeletePte(IN PMMPTE PointerPte,
+ IN PVOID VirtualAddress,
+ IN PEPROCESS CurrentProcess,
+ IN PMMPTE PrototypePte)
+{
+ PMMPFN Pfn1;
+ MMPTE TempPte;
+ PFN_NUMBER PageFrameIndex;
+ PMMPDE PointerPde;
+
+ /* PFN lock must be held */
+ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ /* Capture the PTE */
+ TempPte = *PointerPte;
+
+ /* We only support valid PTEs for now */
+ ASSERT(TempPte.u.Hard.Valid == 1);
+ if (TempPte.u.Hard.Valid == 0)
+ {
+ /* Invalid PTEs not supported yet */
+ ASSERT(TempPte.u.Soft.Prototype == 0);
+ ASSERT(TempPte.u.Soft.Transition == 0);
+ }
+
+ /* Get the PFN entry */
+ PageFrameIndex = PFN_FROM_PTE(&TempPte);
+ Pfn1 = MiGetPfnEntry(PageFrameIndex);
+
+ /* Check if this is a valid, prototype PTE */
+ if (Pfn1->u3.e1.PrototypePte == 1)
+ {
+ /* Get the PDE and make sure it's faulted in */
+ PointerPde = MiAddressToPde(PointerPte);
+ if (PointerPde->u.Hard.Valid == 0)
+ {
+#if (_MI_PAGING_LEVELS == 2)
+ /* Could be paged pool access from a new process -- synchronize the page directories */
+ if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress)))
+ {
+#endif
+ /* The PDE must be valid at this point */
+ KeBugCheckEx(MEMORY_MANAGEMENT,
+ 0x61940,
+ (ULONG_PTR)PointerPte,
+ PointerPte->u.Long,
+ (ULONG_PTR)VirtualAddress);
+ }
+#if (_MI_PAGING_LEVELS == 2)
+ }
+#endif
+ /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
+ //MiDecrementShareCount(MiGetPfnEntry(PFN_FROM_PTE(PointerPde)), PFN_FROM_PDE(PointerPde));
+
+ /* Drop the share count */
+ MiDecrementShareCount(Pfn1, PageFrameIndex);
+
+ /* No fork yet */
+ if (PointerPte <= MiHighestUserPte) ASSERT(PrototypePte == Pfn1->PteAddress);
+ }
+ else
+ {
+ /* Make sure the saved PTE address is valid */
+ if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte)
+ {
+ /* The PFN entry is illegal, or invalid */
+ KeBugCheckEx(MEMORY_MANAGEMENT,
+ 0x401,
+ (ULONG_PTR)PointerPte,
+ PointerPte->u.Long,
+ (ULONG_PTR)Pfn1->PteAddress);
+ }
+
+ /* There should only be 1 shared reference count */
+ ASSERT(Pfn1->u2.ShareCount == 1);
+
+ /* FIXME: Drop the reference on the page table. For now, leak it until RosMM is gone */
+ //MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame);
+
+ /* Mark the PFN for deletion and dereference what should be the last ref */
+ MI_SET_PFN_DELETED(Pfn1);
+ MiDecrementShareCount(Pfn1, PageFrameIndex);
+
+ /* We should eventually do this */
+ //CurrentProcess->NumberOfPrivatePages--;
+ }
+
+ /* Destroy the PTE and flush the TLB */
+ PointerPte->u.Long = 0;
+ KeFlushCurrentTb();
+}
+
+VOID
+NTAPI
+MiDeleteVirtualAddresses(IN ULONG_PTR Va,
+ IN ULONG_PTR EndingAddress,
+ IN PMMVAD Vad)
+{
+ PMMPTE PointerPte, PointerPde, PrototypePte, LastPrototypePte;
+ MMPTE TempPte;
+ PEPROCESS CurrentProcess;
+ KIRQL OldIrql;
+ BOOLEAN AddressGap = FALSE;
+ PSUBSECTION Subsection;
+
+ /* Get out if this is a fake VAD, RosMm will free the marea pages */
+ if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return;
+
+ /* Grab the process and PTE/PDE for the address being deleted */
+ CurrentProcess = PsGetCurrentProcess();
+ PointerPde = MiAddressToPde(Va);
+ PointerPte = MiAddressToPte(Va);
+
+ /* Check if this is a section VAD or a VM VAD */
+ if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte))
+ {
+ /* Don't worry about prototypes */
+ PrototypePte = LastPrototypePte = NULL;
+ }
+ else
+ {
+ /* Get the prototype PTE */
+ PrototypePte = Vad->FirstPrototypePte;
+ LastPrototypePte = Vad->FirstPrototypePte + 1;
+ }
+
+ /* In all cases, we don't support fork() yet */
+ ASSERT(CurrentProcess->CloneRoot == NULL);
+
+ /* Loop the PTE for each VA */
+ while (TRUE)
+ {
+ /* First keep going until we find a valid PDE */
+ while (!PointerPde->u.Long)
+ {
+ /* There are gaps in the address space */
+ AddressGap = TRUE;
+
+ /* Still no valid PDE, try the next 4MB (or whatever) */
+ PointerPde++;
+
+ /* Update the PTE on this new boundary */
+ PointerPte = MiPteToAddress(PointerPde);
+
+ /* Check if all the PDEs are invalid, so there's nothing to free */
+ Va = (ULONG_PTR)MiPteToAddress(PointerPte);
+ if (Va > EndingAddress) return;
+ }
+
+ /* Now check if the PDE is mapped in */
+ if (!PointerPde->u.Hard.Valid)
+ {
+ /* It isn't, so map it in */
+ PointerPte = MiPteToAddress(PointerPde);
+ MiMakeSystemAddressValid(PointerPte, CurrentProcess);
+ }
+
+ /* Now we should have a valid PDE, mapped in, and still have some VA */
+ ASSERT(PointerPde->u.Hard.Valid == 1);
+ ASSERT(Va <= EndingAddress);
+
+ /* Check if this is a section VAD with gaps in it */
+ if ((AddressGap) && (LastPrototypePte))
+ {
+ /* We need to skip to the next correct prototype PTE */
+ PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
+
+ /* And we need the subsection to skip to the next last prototype PTE */
+ Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
+ if (Subsection)
+ {
+ /* Found it! */
+ LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
+ }
+ else
+ {
+ /* No more subsections, we are done with prototype PTEs */
+ PrototypePte = NULL;
+ }
+ }
+
+ /* Lock the PFN Database while we delete the PTEs */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+ do
+ {
+ /* Capture the PDE and make sure it exists */
+ TempPte = *PointerPte;
+ if (TempPte.u.Long)
+ {
+ /* Check if the PTE is actually mapped in */
+ if (TempPte.u.Long & 0xFFFFFC01)
+ {
+ /* Are we dealing with section VAD? */
+ if ((LastPrototypePte) && (PrototypePte > LastPrototypePte))
+ {
+ /* We need to skip to the next correct prototype PTE */
+ PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT);
+
+ /* And we need the subsection to skip to the next last prototype PTE */
+ Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT);
+ if (Subsection)
+ {
+ /* Found it! */
+ LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection];
+ }
+ else
+ {
+ /* No more subsections, we are done with prototype PTEs */
+ PrototypePte = NULL;
+ }
+ }
+
+ /* Check for prototype PTE */
+ if ((TempPte.u.Hard.Valid == 0) &&
+ (TempPte.u.Soft.Prototype == 1))
+ {
+ /* Just nuke it */
+ PointerPte->u.Long = 0;
+ }
+ else
+ {
+ /* Delete the PTE proper */
+ MiDeletePte(PointerPte,
+ (PVOID)Va,
+ CurrentProcess,
+ PrototypePte);
+ }
+ }
+ else
+ {
+ /* The PTE was never mapped, just nuke it here */
+ PointerPte->u.Long = 0;
+ }
+ }
+
+ /* Update the address and PTE for it */
+ Va += PAGE_SIZE;
+ PointerPte++;
+ PrototypePte++;
+
+ /* Making sure the PDE is still valid */
+ ASSERT(PointerPde->u.Hard.Valid == 1);
+ }
+ while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress));
+
+ /* The PDE should still be valid at this point */
+ ASSERT(PointerPde->u.Hard.Valid == 1);
+
+ /* Release the lock and get out if we're done */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ if (Va > EndingAddress) return;
+
+ /* Otherwise, we exited because we hit a new PDE boundary, so start over */
+ PointerPde = MiAddressToPde(Va);
+ AddressGap = FALSE;
+ }
+}
+
LONG
MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo,
OUT PBOOLEAN HaveBadAddress,
return STATUS_SUCCESS;
}
+ULONG
+NTAPI
+MiQueryAddressState(IN PVOID Va,
+ IN PMMVAD Vad,
+ IN PEPROCESS TargetProcess,
+ OUT PULONG ReturnedProtect,
+ OUT PVOID *NextVa)
+{
+
+ PMMPTE PointerPte, PointerPde;
+ MMPTE TempPte;
+ BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE;
+ ULONG State = MEM_RESERVE, Protect = 0, LockChange;
+ ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) &&
+ (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT)));
+
+ /* Only normal VADs supported */
+ ASSERT(Vad->u.VadFlags.VadType == VadNone);
+
+ /* Get the PDE and PTE for the address */
+ PointerPde = MiAddressToPde(Va);
+ PointerPte = MiAddressToPte(Va);
+
+ /* Return the next range */
+ *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE);
+
+ /* Loop to make sure the PDE is valid */
+ do
+ {
+ /* Try again */
+ LockChange = 0;
+
+ /* Is the PDE empty? */
+ if (!PointerPde->u.Long)
+ {
+ /* No address in this range used yet, move to the next PDE range */
+ *NextVa = MiPteToAddress(MiPteToAddress(PointerPde + 1));
+ break;
+ }
+
+ /* The PDE is empty, but is it faulted in? */
+ if (!PointerPde->u.Hard.Valid)
+ {
+ /* It isn't, go ahead and do the fault */
+ LockChange = MiMakeSystemAddressValid(MiPteToAddress(PointerPde),
+ TargetProcess);
+ }
+
+ /* Check if the PDE was faulted in, making the PTE readable */
+ if (!LockChange) ValidPte = TRUE;
+ } while (LockChange);
+
+ /* Is it safe to try reading the PTE? */
+ if (ValidPte)
+ {
+ /* FIXME: watch out for large pages */
+
+ /* Capture the PTE */
+ TempPte = *PointerPte;
+ if (TempPte.u.Long)
+ {
+ /* The PTE is valid, so it's not zeroed out */
+ DemandZeroPte = FALSE;
+
+ /* Check if it's valid or has a valid protection mask */
+ ASSERT(TempPte.u.Soft.Prototype == 0);
+ if ((TempPte.u.Soft.Protection != MM_DECOMMIT) ||
+ (TempPte.u.Hard.Valid == 1))
+ {
+ /* This means it's committed */
+ State = MEM_COMMIT;
+
+ /* For now, we lie about the protection */
+ Protect = PAGE_EXECUTE_READWRITE;
+ }
+ else
+ {
+ /* Otherwise our defaults should hold */
+ ASSERT(Protect == 0);
+ ASSERT(State == MEM_RESERVE);
+ }
+ }
+ }
+
+ /* Check if this was a demand-zero PTE, since we need to find the state */
+ if (DemandZeroPte)
+ {
+ /* Check if the VAD is for committed memory */
+ if (Vad->u.VadFlags.MemCommit)
+ {
+ /* This is committed memory */
+ State = MEM_COMMIT;
+
+ /* Convert the protection */
+ Protect = MmProtectToValue[Vad->u.VadFlags.Protection];
+ }
+ }
+
+ /* Return the protection code */
+ *ReturnedProtect = Protect;
+ return State;
+}
+
/* PUBLIC FUNCTIONS ***********************************************************/
/*
return STATUS_SUCCESS;
}
+NTSTATUS
+NTAPI
+NtQueryVirtualMemory(IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress,
+ IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
+ OUT PVOID MemoryInformation,
+ IN SIZE_T MemoryInformationLength,
+ OUT PSIZE_T ReturnLength)
+{
+ PEPROCESS TargetProcess;
+ NTSTATUS Status;
+ PMMVAD Vad = NULL;
+ PVOID Address, NextAddress;
+ BOOLEAN Found;
+ ULONG NewProtect, NewState, BaseVpn;
+ MEMORY_BASIC_INFORMATION MemoryInfo;
+ KAPC_STATE ApcState;
+ DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress);
+
+ /* Only this class is supported for now */
+ ASSERT(MemoryInformationClass == MemoryBasicInformation);
+
+ /* Validate the size information of the class */
+ if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION))
+ {
+ /* The size is invalid */
+ return STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ /* Bail out if the address is invalid */
+ if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER;
+
+ /* Check for illegal addresses in user-space, or the shared memory area */
+ if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) ||
+ (PAGE_ALIGN(BaseAddress) == (PVOID)USER_SHARED_DATA))
+ {
+ /* FIXME: We should return some bogus info structure */
+ UNIMPLEMENTED;
+ while (TRUE);
+ }
+
+ /* Check if this is for a local or remote process */
+ if (ProcessHandle == NtCurrentProcess())
+ {
+ TargetProcess = PsGetCurrentProcess();
+ }
+ else
+ {
+ /* Reference the target process */
+ Status = ObReferenceObjectByHandle(ProcessHandle,
+ PROCESS_QUERY_INFORMATION,
+ PsProcessType,
+ ExGetPreviousMode(),
+ (PVOID*)&TargetProcess,
+ NULL);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Attach to it now */
+ KeStackAttachProcess(&TargetProcess->Pcb, &ApcState);
+ }
+
+ /* Loop the VADs */
+ ASSERT(TargetProcess->VadRoot.NumberGenericTableElements);
+ if (TargetProcess->VadRoot.NumberGenericTableElements)
+ {
+ /* Scan on the right */
+ Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild;
+ BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT;
+ while (Vad)
+ {
+ /* Check if this VAD covers the allocation range */
+ if ((BaseVpn >= Vad->StartingVpn) &&
+ (BaseVpn <= Vad->EndingVpn))
+ {
+ /* We're done */
+ Found = TRUE;
+ break;
+ }
+
+ /* Check if this VAD is too high */
+ if (BaseVpn < Vad->StartingVpn)
+ {
+ /* Search on the left next */
+ Vad = Vad->LeftChild;
+ }
+ else
+ {
+ /* Then this VAD is too low, keep searching on the right */
+ ASSERT(BaseVpn > Vad->EndingVpn);
+ Vad = Vad->RightChild;
+ }
+ }
+ }
+
+ /* Was a VAD found? */
+ if (!Found)
+ {
+ /* We don't handle this yet */
+ UNIMPLEMENTED;
+ while (TRUE);
+ }
+
+ /* This must be a VM VAD */
+ ASSERT(Vad->u.VadFlags.PrivateMemory);
+
+ /* Build the initial information block */
+ Address = PAGE_ALIGN(BaseAddress);
+ MemoryInfo.BaseAddress = Address;
+ MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT);
+ MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection];
+ MemoryInfo.Type = MEM_PRIVATE;
+
+ /* Find the largest chunk of memory which has the same state and protection mask */
+ MemoryInfo.State = MiQueryAddressState(Address,
+ Vad,
+ TargetProcess,
+ &MemoryInfo.Protect,
+ &NextAddress);
+ Address = NextAddress;
+ while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn)
+ {
+ /* Keep going unless the state or protection mask changed */
+ NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress);
+ if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break;
+ Address = NextAddress;
+ }
+
+ /* Now that we know the last VA address, calculate hte region size */
+ MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress);
+
+ /* Check if we were attached */
+ if (ProcessHandle != NtCurrentProcess())
+ {
+ /* Detach and derefernece the process */
+ KeUnstackDetachProcess(&ApcState);
+ ObDereferenceObject(TargetProcess);
+ }
+
+ /* Return the data (FIXME: Use SEH) */
+ *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo;
+ if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION);
+
+ /* All went well */
+ DPRINT("Base: %p AllocBase: %p Protect: %lx AllocProtect: %lx "
+ "State: %lx Type: %lx Size: %lx\n",
+ MemoryInfo.BaseAddress, MemoryInfo.AllocationBase,
+ MemoryInfo.AllocationProtect, MemoryInfo.Protect,
+ MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize);
+ return STATUS_SUCCESS;
+}
+
/* EOF */