Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenvirtual.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Kernel 00003 * LICENSE: BSD - See COPYING.ARM in the top level directory 00004 * FILE: ntoskrnl/mm/ARM3/virtual.c 00005 * PURPOSE: ARM Memory Manager Virtual Memory Management 00006 * PROGRAMMERS: ReactOS Portable Systems Group 00007 */ 00008 00009 /* INCLUDES *******************************************************************/ 00010 00011 #include <ntoskrnl.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 #define MODULE_INVOLVED_IN_ARM3 00016 #include "../ARM3/miarm.h" 00017 00018 #define MI_MAPPED_COPY_PAGES 14 00019 #define MI_POOL_COPY_BYTES 512 00020 #define MI_MAX_TRANSFER_SIZE 64 * 1024 00021 00022 NTSTATUS NTAPI 00023 MiProtectVirtualMemory(IN PEPROCESS Process, 00024 IN OUT PVOID *BaseAddress, 00025 IN OUT PSIZE_T NumberOfBytesToProtect, 00026 IN ULONG NewAccessProtection, 00027 OUT PULONG OldAccessProtection OPTIONAL); 00028 00029 /* PRIVATE FUNCTIONS **********************************************************/ 00030 00031 ULONG 00032 NTAPI 00033 MiCalculatePageCommitment(IN ULONG_PTR StartingAddress, 00034 IN ULONG_PTR EndingAddress, 00035 IN PMMVAD Vad, 00036 IN PEPROCESS Process) 00037 { 00038 PMMPTE PointerPte, LastPte, PointerPde; 00039 ULONG CommittedPages; 00040 00041 /* Compute starting and ending PTE and PDE addresses */ 00042 PointerPde = MiAddressToPde(StartingAddress); 00043 PointerPte = MiAddressToPte(StartingAddress); 00044 LastPte = MiAddressToPte(EndingAddress); 00045 00046 /* Handle commited pages first */ 00047 if (Vad->u.VadFlags.MemCommit == 1) 00048 { 00049 /* This is a committed VAD, so Assume the whole range is committed */ 00050 CommittedPages = BYTES_TO_PAGES(EndingAddress - StartingAddress); 00051 00052 /* Is the PDE demand-zero? */ 00053 PointerPde = MiAddressToPte(PointerPte); 00054 if (PointerPde->u.Long != 0) 00055 { 00056 /* It is not. Is it valid? */ 00057 if (PointerPde->u.Hard.Valid == 0) 00058 { 00059 /* Fault it in */ 00060 PointerPte = MiPteToAddress(PointerPde); 00061 MiMakeSystemAddressValid(PointerPte, Process); 00062 } 00063 } 00064 else 00065 { 00066 /* It is, skip it and move to the next PDE, unless we're done */ 00067 PointerPde++; 00068 PointerPte = MiPteToAddress(PointerPde); 00069 if (PointerPte > LastPte) return CommittedPages; 00070 } 00071 00072 /* Now loop all the PTEs in the range */ 00073 while (PointerPte <= LastPte) 00074 { 00075 /* Have we crossed a PDE boundary? */ 00076 if (MiIsPteOnPdeBoundary(PointerPte)) 00077 { 00078 /* Is this PDE demand zero? */ 00079 PointerPde = MiAddressToPte(PointerPte); 00080 if (PointerPde->u.Long != 0) 00081 { 00082 /* It isn't -- is it valid? */ 00083 if (PointerPde->u.Hard.Valid == 0) 00084 { 00085 /* Nope, fault it in */ 00086 PointerPte = MiPteToAddress(PointerPde); 00087 MiMakeSystemAddressValid(PointerPte, Process); 00088 } 00089 } 00090 else 00091 { 00092 /* It is, skip it and move to the next PDE */ 00093 PointerPde++; 00094 PointerPte = MiPteToAddress(PointerPde); 00095 continue; 00096 } 00097 } 00098 00099 /* Is this PTE demand zero? */ 00100 if (PointerPte->u.Long != 0) 00101 { 00102 /* It isn't -- is it a decommited, invalid, or faulted PTE? */ 00103 if ((PointerPte->u.Soft.Protection == MM_DECOMMIT) && 00104 (PointerPte->u.Hard.Valid == 0) && 00105 ((PointerPte->u.Soft.Prototype == 0) || 00106 (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))) 00107 { 00108 /* It is, so remove it from the count of commited pages */ 00109 CommittedPages--; 00110 } 00111 } 00112 00113 /* Move to the next PTE */ 00114 PointerPte++; 00115 } 00116 00117 /* Return how many committed pages there still are */ 00118 return CommittedPages; 00119 } 00120 00121 /* This is a non-commited VAD, so assume none of it is committed */ 00122 CommittedPages = 0; 00123 00124 /* Is the PDE demand-zero? */ 00125 PointerPde = MiAddressToPte(PointerPte); 00126 if (PointerPde->u.Long != 0) 00127 { 00128 /* It isn't -- is it invalid? */ 00129 if (PointerPde->u.Hard.Valid == 0) 00130 { 00131 /* It is, so page it in */ 00132 PointerPte = MiPteToAddress(PointerPde); 00133 MiMakeSystemAddressValid(PointerPte, Process); 00134 } 00135 } 00136 else 00137 { 00138 /* It is, so skip it and move to the next PDE */ 00139 PointerPde++; 00140 PointerPte = MiPteToAddress(PointerPde); 00141 if (PointerPte > LastPte) return CommittedPages; 00142 } 00143 00144 /* Loop all the PTEs in this PDE */ 00145 while (PointerPte <= LastPte) 00146 { 00147 /* Have we crossed a PDE boundary? */ 00148 if (MiIsPteOnPdeBoundary(PointerPte)) 00149 { 00150 /* Is this new PDE demand-zero? */ 00151 PointerPde = MiAddressToPte(PointerPte); 00152 if (PointerPde->u.Long != 0) 00153 { 00154 /* It isn't. Is it valid? */ 00155 if (PointerPde->u.Hard.Valid == 0) 00156 { 00157 /* It isn't, so make it valid */ 00158 PointerPte = MiPteToAddress(PointerPde); 00159 MiMakeSystemAddressValid(PointerPte, Process); 00160 } 00161 } 00162 else 00163 { 00164 /* It is, so skip it and move to the next one */ 00165 PointerPde++; 00166 PointerPte = MiPteToAddress(PointerPde); 00167 continue; 00168 } 00169 } 00170 00171 /* Is this PTE demand-zero? */ 00172 if (PointerPte->u.Long != 0) 00173 { 00174 /* Nope. Is it a valid, non-decommited, non-paged out PTE? */ 00175 if ((PointerPte->u.Soft.Protection != MM_DECOMMIT) || 00176 (PointerPte->u.Hard.Valid == 1) || 00177 ((PointerPte->u.Soft.Prototype == 1) && 00178 (PointerPte->u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED))) 00179 { 00180 /* It is! So we'll treat this as a committed page */ 00181 CommittedPages++; 00182 } 00183 } 00184 00185 /* Move to the next PTE */ 00186 PointerPte++; 00187 } 00188 00189 /* Return how many committed pages we found in this VAD */ 00190 return CommittedPages; 00191 } 00192 00193 ULONG 00194 NTAPI 00195 MiMakeSystemAddressValid(IN PVOID PageTableVirtualAddress, 00196 IN PEPROCESS CurrentProcess) 00197 { 00198 NTSTATUS Status; 00199 BOOLEAN WsWasLocked = FALSE, LockChange = FALSE; 00200 PETHREAD CurrentThread = PsGetCurrentThread(); 00201 00202 /* Must be a non-pool page table, since those are double-mapped already */ 00203 ASSERT(PageTableVirtualAddress > MM_HIGHEST_USER_ADDRESS); 00204 ASSERT((PageTableVirtualAddress < MmPagedPoolStart) || 00205 (PageTableVirtualAddress > MmPagedPoolEnd)); 00206 00207 /* Working set lock or PFN lock should be held */ 00208 ASSERT(KeAreAllApcsDisabled() == TRUE); 00209 00210 /* Check if the page table is valid */ 00211 while (!MmIsAddressValid(PageTableVirtualAddress)) 00212 { 00213 /* Check if the WS is locked */ 00214 if (CurrentThread->OwnsProcessWorkingSetExclusive) 00215 { 00216 /* Unlock the working set and remember it was locked */ 00217 MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread); 00218 WsWasLocked = TRUE; 00219 } 00220 00221 /* Fault it in */ 00222 Status = MmAccessFault(FALSE, PageTableVirtualAddress, KernelMode, NULL); 00223 if (!NT_SUCCESS(Status)) 00224 { 00225 /* This should not fail */ 00226 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR, 00227 1, 00228 Status, 00229 (ULONG_PTR)CurrentProcess, 00230 (ULONG_PTR)PageTableVirtualAddress); 00231 } 00232 00233 /* Lock the working set again */ 00234 if (WsWasLocked) MiLockProcessWorkingSet(CurrentProcess, CurrentThread); 00235 00236 /* This flag will be useful later when we do better locking */ 00237 LockChange = TRUE; 00238 } 00239 00240 /* Let caller know what the lock state is */ 00241 return LockChange; 00242 } 00243 00244 ULONG 00245 NTAPI 00246 MiMakeSystemAddressValidPfn(IN PVOID VirtualAddress, 00247 IN KIRQL OldIrql) 00248 { 00249 NTSTATUS Status; 00250 BOOLEAN LockChange = FALSE; 00251 00252 /* Must be e kernel address */ 00253 ASSERT(VirtualAddress > MM_HIGHEST_USER_ADDRESS); 00254 00255 /* Check if the page is valid */ 00256 while (!MmIsAddressValid(VirtualAddress)) 00257 { 00258 /* Release the PFN database */ 00259 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00260 00261 /* Fault it in */ 00262 Status = MmAccessFault(FALSE, VirtualAddress, KernelMode, NULL); 00263 if (!NT_SUCCESS(Status)) 00264 { 00265 /* This should not fail */ 00266 KeBugCheckEx(KERNEL_DATA_INPAGE_ERROR, 00267 3, 00268 Status, 00269 0, 00270 (ULONG_PTR)VirtualAddress); 00271 } 00272 00273 /* This flag will be useful later when we do better locking */ 00274 LockChange = TRUE; 00275 00276 /* Lock the PFN database */ 00277 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00278 } 00279 00280 /* Let caller know what the lock state is */ 00281 return LockChange; 00282 } 00283 00284 PFN_COUNT 00285 NTAPI 00286 MiDeleteSystemPageableVm(IN PMMPTE PointerPte, 00287 IN PFN_NUMBER PageCount, 00288 IN ULONG Flags, 00289 OUT PPFN_NUMBER ValidPages) 00290 { 00291 PFN_COUNT ActualPages = 0; 00292 PETHREAD CurrentThread = PsGetCurrentThread(); 00293 PMMPFN Pfn1, Pfn2; 00294 PFN_NUMBER PageFrameIndex, PageTableIndex; 00295 KIRQL OldIrql; 00296 ASSERT(KeGetCurrentIrql() <= APC_LEVEL); 00297 00298 /* Lock the system working set */ 00299 MiLockWorkingSet(CurrentThread, &MmSystemCacheWs); 00300 00301 /* Loop all pages */ 00302 while (PageCount) 00303 { 00304 /* Make sure there's some data about the page */ 00305 if (PointerPte->u.Long) 00306 { 00307 /* As always, only handle current ARM3 scenarios */ 00308 ASSERT(PointerPte->u.Soft.Prototype == 0); 00309 ASSERT(PointerPte->u.Soft.Transition == 0); 00310 00311 /* Normally this is one possibility -- freeing a valid page */ 00312 if (PointerPte->u.Hard.Valid) 00313 { 00314 /* Get the page PFN */ 00315 PageFrameIndex = PFN_FROM_PTE(PointerPte); 00316 Pfn1 = MiGetPfnEntry(PageFrameIndex); 00317 00318 /* Should not have any working set data yet */ 00319 ASSERT(Pfn1->u1.WsIndex == 0); 00320 00321 /* Actual valid, legitimate, pages */ 00322 if (ValidPages) (*ValidPages)++; 00323 00324 /* Get the page table entry */ 00325 PageTableIndex = Pfn1->u4.PteFrame; 00326 Pfn2 = MiGetPfnEntry(PageTableIndex); 00327 00328 /* Lock the PFN database */ 00329 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00330 00331 /* Delete it the page */ 00332 MI_SET_PFN_DELETED(Pfn1); 00333 MiDecrementShareCount(Pfn1, PageFrameIndex); 00334 00335 /* Decrement the page table too */ 00336 MiDecrementShareCount(Pfn2, PageTableIndex); 00337 00338 /* Release the PFN database */ 00339 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00340 00341 /* Destroy the PTE */ 00342 PointerPte->u.Long = 0; 00343 } 00344 00345 /* Actual legitimate pages */ 00346 ActualPages++; 00347 } 00348 else 00349 { 00350 /* 00351 * The only other ARM3 possibility is a demand zero page, which would 00352 * mean freeing some of the paged pool pages that haven't even been 00353 * touched yet, as part of a larger allocation. 00354 * 00355 * Right now, we shouldn't expect any page file information in the PTE 00356 */ 00357 ASSERT(PointerPte->u.Soft.PageFileHigh == 0); 00358 00359 /* Destroy the PTE */ 00360 PointerPte->u.Long = 0; 00361 } 00362 00363 /* Keep going */ 00364 PointerPte++; 00365 PageCount--; 00366 } 00367 00368 /* Release the working set */ 00369 MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs); 00370 00371 /* Flush the entire TLB */ 00372 KeFlushEntireTb(TRUE, TRUE); 00373 00374 /* Done */ 00375 return ActualPages; 00376 } 00377 00378 VOID 00379 NTAPI 00380 MiDeletePte(IN PMMPTE PointerPte, 00381 IN PVOID VirtualAddress, 00382 IN PEPROCESS CurrentProcess, 00383 IN PMMPTE PrototypePte) 00384 { 00385 PMMPFN Pfn1; 00386 MMPTE TempPte; 00387 PFN_NUMBER PageFrameIndex; 00388 PMMPDE PointerPde; 00389 00390 /* PFN lock must be held */ 00391 ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); 00392 00393 /* Capture the PTE */ 00394 TempPte = *PointerPte; 00395 00396 /* We only support valid PTEs for now */ 00397 ASSERT(TempPte.u.Hard.Valid == 1); 00398 if (TempPte.u.Hard.Valid == 0) 00399 { 00400 /* Invalid PTEs not supported yet */ 00401 ASSERT(TempPte.u.Soft.Prototype == 0); 00402 ASSERT(TempPte.u.Soft.Transition == 0); 00403 } 00404 00405 /* Get the PFN entry */ 00406 PageFrameIndex = PFN_FROM_PTE(&TempPte); 00407 Pfn1 = MiGetPfnEntry(PageFrameIndex); 00408 00409 /* Check if this is a valid, prototype PTE */ 00410 if (Pfn1->u3.e1.PrototypePte == 1) 00411 { 00412 /* Get the PDE and make sure it's faulted in */ 00413 PointerPde = MiPteToPde(PointerPte); 00414 if (PointerPde->u.Hard.Valid == 0) 00415 { 00416 #if (_MI_PAGING_LEVELS == 2) 00417 /* Could be paged pool access from a new process -- synchronize the page directories */ 00418 if (!NT_SUCCESS(MiCheckPdeForPagedPool(VirtualAddress))) 00419 { 00420 #endif 00421 /* The PDE must be valid at this point */ 00422 KeBugCheckEx(MEMORY_MANAGEMENT, 00423 0x61940, 00424 (ULONG_PTR)PointerPte, 00425 PointerPte->u.Long, 00426 (ULONG_PTR)VirtualAddress); 00427 } 00428 #if (_MI_PAGING_LEVELS == 2) 00429 } 00430 #endif 00431 /* Drop the share count */ 00432 MiDecrementShareCount(Pfn1, PageFrameIndex); 00433 00434 /* Either a fork, or this is the shared user data page */ 00435 if ((PointerPte <= MiHighestUserPte) && (PrototypePte != Pfn1->PteAddress)) 00436 { 00437 /* If it's not the shared user page, then crash, since there's no fork() yet */ 00438 if ((PAGE_ALIGN(VirtualAddress) != (PVOID)USER_SHARED_DATA) || 00439 (MmHighestUserAddress <= (PVOID)USER_SHARED_DATA)) 00440 { 00441 /* Must be some sort of memory corruption */ 00442 KeBugCheckEx(MEMORY_MANAGEMENT, 00443 0x400, 00444 (ULONG_PTR)PointerPte, 00445 (ULONG_PTR)PrototypePte, 00446 (ULONG_PTR)Pfn1->PteAddress); 00447 } 00448 } 00449 } 00450 else 00451 { 00452 /* Make sure the saved PTE address is valid */ 00453 if ((PMMPTE)((ULONG_PTR)Pfn1->PteAddress & ~0x1) != PointerPte) 00454 { 00455 /* The PFN entry is illegal, or invalid */ 00456 KeBugCheckEx(MEMORY_MANAGEMENT, 00457 0x401, 00458 (ULONG_PTR)PointerPte, 00459 PointerPte->u.Long, 00460 (ULONG_PTR)Pfn1->PteAddress); 00461 } 00462 00463 /* There should only be 1 shared reference count */ 00464 ASSERT(Pfn1->u2.ShareCount == 1); 00465 00466 /* Drop the reference on the page table. */ 00467 MiDecrementShareCount(MiGetPfnEntry(Pfn1->u4.PteFrame), Pfn1->u4.PteFrame); 00468 00469 /* Mark the PFN for deletion and dereference what should be the last ref */ 00470 MI_SET_PFN_DELETED(Pfn1); 00471 MiDecrementShareCount(Pfn1, PageFrameIndex); 00472 00473 /* We should eventually do this */ 00474 //CurrentProcess->NumberOfPrivatePages--; 00475 } 00476 00477 /* Destroy the PTE and flush the TLB */ 00478 PointerPte->u.Long = 0; 00479 KeFlushCurrentTb(); 00480 } 00481 00482 VOID 00483 NTAPI 00484 MiDeleteVirtualAddresses(IN ULONG_PTR Va, 00485 IN ULONG_PTR EndingAddress, 00486 IN PMMVAD Vad) 00487 { 00488 PMMPTE PointerPte, PrototypePte, LastPrototypePte; 00489 PMMPDE PointerPde; 00490 MMPTE TempPte; 00491 PEPROCESS CurrentProcess; 00492 KIRQL OldIrql; 00493 BOOLEAN AddressGap = FALSE; 00494 PSUBSECTION Subsection; 00495 PUSHORT UsedPageTableEntries; 00496 00497 /* Get out if this is a fake VAD, RosMm will free the marea pages */ 00498 if ((Vad) && (Vad->u.VadFlags.Spare == 1)) return; 00499 00500 /* Grab the process and PTE/PDE for the address being deleted */ 00501 CurrentProcess = PsGetCurrentProcess(); 00502 PointerPde = MiAddressToPde(Va); 00503 PointerPte = MiAddressToPte(Va); 00504 00505 /* Check if this is a section VAD or a VM VAD */ 00506 if (!(Vad) || (Vad->u.VadFlags.PrivateMemory) || !(Vad->FirstPrototypePte)) 00507 { 00508 /* Don't worry about prototypes */ 00509 PrototypePte = LastPrototypePte = NULL; 00510 } 00511 else 00512 { 00513 /* Get the prototype PTE */ 00514 PrototypePte = Vad->FirstPrototypePte; 00515 LastPrototypePte = Vad->FirstPrototypePte + 1; 00516 } 00517 00518 /* In all cases, we don't support fork() yet */ 00519 ASSERT(CurrentProcess->CloneRoot == NULL); 00520 00521 /* Loop the PTE for each VA */ 00522 while (TRUE) 00523 { 00524 /* First keep going until we find a valid PDE */ 00525 while (!PointerPde->u.Long) 00526 { 00527 /* There are gaps in the address space */ 00528 AddressGap = TRUE; 00529 00530 /* Still no valid PDE, try the next 4MB (or whatever) */ 00531 PointerPde++; 00532 00533 /* Update the PTE on this new boundary */ 00534 PointerPte = MiPteToAddress(PointerPde); 00535 00536 /* Check if all the PDEs are invalid, so there's nothing to free */ 00537 Va = (ULONG_PTR)MiPteToAddress(PointerPte); 00538 if (Va > EndingAddress) return; 00539 } 00540 00541 /* Now check if the PDE is mapped in */ 00542 if (!PointerPde->u.Hard.Valid) 00543 { 00544 /* It isn't, so map it in */ 00545 PointerPte = MiPteToAddress(PointerPde); 00546 MiMakeSystemAddressValid(PointerPte, CurrentProcess); 00547 } 00548 00549 /* Now we should have a valid PDE, mapped in, and still have some VA */ 00550 ASSERT(PointerPde->u.Hard.Valid == 1); 00551 ASSERT(Va <= EndingAddress); 00552 UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Va)]; 00553 00554 /* Check if this is a section VAD with gaps in it */ 00555 if ((AddressGap) && (LastPrototypePte)) 00556 { 00557 /* We need to skip to the next correct prototype PTE */ 00558 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); 00559 00560 /* And we need the subsection to skip to the next last prototype PTE */ 00561 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); 00562 if (Subsection) 00563 { 00564 /* Found it! */ 00565 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; 00566 } 00567 else 00568 { 00569 /* No more subsections, we are done with prototype PTEs */ 00570 PrototypePte = NULL; 00571 } 00572 } 00573 00574 /* Lock the PFN Database while we delete the PTEs */ 00575 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 00576 do 00577 { 00578 /* Capture the PDE and make sure it exists */ 00579 TempPte = *PointerPte; 00580 if (TempPte.u.Long) 00581 { 00582 DPRINT("Decrement used PTEs by address: %lx\n", Va); 00583 (*UsedPageTableEntries)--; 00584 ASSERT((*UsedPageTableEntries) < PTE_COUNT); 00585 DPRINT("Refs: %lx\n", (*UsedPageTableEntries)); 00586 00587 /* Check if the PTE is actually mapped in */ 00588 if (TempPte.u.Long & 0xFFFFFC01) 00589 { 00590 /* Are we dealing with section VAD? */ 00591 if ((LastPrototypePte) && (PrototypePte > LastPrototypePte)) 00592 { 00593 /* We need to skip to the next correct prototype PTE */ 00594 PrototypePte = MI_GET_PROTOTYPE_PTE_FOR_VPN(Vad, Va >> PAGE_SHIFT); 00595 00596 /* And we need the subsection to skip to the next last prototype PTE */ 00597 Subsection = MiLocateSubsection(Vad, Va >> PAGE_SHIFT); 00598 if (Subsection) 00599 { 00600 /* Found it! */ 00601 LastPrototypePte = &Subsection->SubsectionBase[Subsection->PtesInSubsection]; 00602 } 00603 else 00604 { 00605 /* No more subsections, we are done with prototype PTEs */ 00606 PrototypePte = NULL; 00607 } 00608 } 00609 00610 /* Check for prototype PTE */ 00611 if ((TempPte.u.Hard.Valid == 0) && 00612 (TempPte.u.Soft.Prototype == 1)) 00613 { 00614 /* Just nuke it */ 00615 PointerPte->u.Long = 0; 00616 } 00617 else 00618 { 00619 /* Delete the PTE proper */ 00620 MiDeletePte(PointerPte, 00621 (PVOID)Va, 00622 CurrentProcess, 00623 PrototypePte); 00624 } 00625 } 00626 else 00627 { 00628 /* The PTE was never mapped, just nuke it here */ 00629 PointerPte->u.Long = 0; 00630 } 00631 } 00632 00633 /* Update the address and PTE for it */ 00634 Va += PAGE_SIZE; 00635 PointerPte++; 00636 PrototypePte++; 00637 00638 /* Making sure the PDE is still valid */ 00639 ASSERT(PointerPde->u.Hard.Valid == 1); 00640 } 00641 while ((Va & (PDE_MAPPED_VA - 1)) && (Va <= EndingAddress)); 00642 00643 /* The PDE should still be valid at this point */ 00644 ASSERT(PointerPde->u.Hard.Valid == 1); 00645 00646 DPRINT("Should check if handles for: %p are zero (PDE: %lx)\n", Va, PointerPde->u.Hard.PageFrameNumber); 00647 if (!(*UsedPageTableEntries)) 00648 { 00649 DPRINT("They are!\n"); 00650 if (PointerPde->u.Long != 0) 00651 { 00652 DPRINT("PDE active: %lx in %16s\n", PointerPde->u.Hard.PageFrameNumber, CurrentProcess->ImageFileName); 00653 00654 /* Delete the PTE proper */ 00655 MiDeletePte(PointerPde, 00656 MiPteToAddress(PointerPde), 00657 CurrentProcess, 00658 NULL); 00659 } 00660 } 00661 00662 /* Release the lock and get out if we're done */ 00663 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 00664 if (Va > EndingAddress) return; 00665 00666 /* Otherwise, we exited because we hit a new PDE boundary, so start over */ 00667 PointerPde = MiAddressToPde(Va); 00668 AddressGap = FALSE; 00669 } 00670 } 00671 00672 LONG 00673 MiGetExceptionInfo(IN PEXCEPTION_POINTERS ExceptionInfo, 00674 OUT PBOOLEAN HaveBadAddress, 00675 OUT PULONG_PTR BadAddress) 00676 { 00677 PEXCEPTION_RECORD ExceptionRecord; 00678 PAGED_CODE(); 00679 00680 // 00681 // Assume default 00682 // 00683 *HaveBadAddress = FALSE; 00684 00685 // 00686 // Get the exception record 00687 // 00688 ExceptionRecord = ExceptionInfo->ExceptionRecord; 00689 00690 // 00691 // Look at the exception code 00692 // 00693 if ((ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) || 00694 (ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) || 00695 (ExceptionRecord->ExceptionCode == STATUS_IN_PAGE_ERROR)) 00696 { 00697 // 00698 // We can tell the address if we have more than one parameter 00699 // 00700 if (ExceptionRecord->NumberParameters > 1) 00701 { 00702 // 00703 // Return the address 00704 // 00705 *HaveBadAddress = TRUE; 00706 *BadAddress = ExceptionRecord->ExceptionInformation[1]; 00707 } 00708 } 00709 00710 // 00711 // Continue executing the next handler 00712 // 00713 return EXCEPTION_EXECUTE_HANDLER; 00714 } 00715 00716 NTSTATUS 00717 NTAPI 00718 MiDoMappedCopy(IN PEPROCESS SourceProcess, 00719 IN PVOID SourceAddress, 00720 IN PEPROCESS TargetProcess, 00721 OUT PVOID TargetAddress, 00722 IN SIZE_T BufferSize, 00723 IN KPROCESSOR_MODE PreviousMode, 00724 OUT PSIZE_T ReturnSize) 00725 { 00726 PFN_NUMBER MdlBuffer[(sizeof(MDL) / sizeof(PFN_NUMBER)) + MI_MAPPED_COPY_PAGES + 1]; 00727 PMDL Mdl = (PMDL)MdlBuffer; 00728 SIZE_T TotalSize, CurrentSize, RemainingSize; 00729 volatile BOOLEAN FailedInProbe = FALSE, FailedInMapping = FALSE, FailedInMoving; 00730 volatile BOOLEAN PagesLocked; 00731 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; 00732 volatile PVOID MdlAddress; 00733 KAPC_STATE ApcState; 00734 BOOLEAN HaveBadAddress; 00735 ULONG_PTR BadAddress; 00736 NTSTATUS Status = STATUS_SUCCESS; 00737 PAGED_CODE(); 00738 00739 // 00740 // Calculate the maximum amount of data to move 00741 // 00742 TotalSize = MI_MAPPED_COPY_PAGES * PAGE_SIZE; 00743 if (BufferSize <= TotalSize) TotalSize = BufferSize; 00744 CurrentSize = TotalSize; 00745 RemainingSize = BufferSize; 00746 00747 // 00748 // Loop as long as there is still data 00749 // 00750 while (RemainingSize > 0) 00751 { 00752 // 00753 // Check if this transfer will finish everything off 00754 // 00755 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; 00756 00757 // 00758 // Attach to the source address space 00759 // 00760 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); 00761 00762 // 00763 // Reset state for this pass 00764 // 00765 MdlAddress = NULL; 00766 PagesLocked = FALSE; 00767 FailedInMoving = FALSE; 00768 ASSERT(FailedInProbe == FALSE); 00769 00770 // 00771 // Protect user-mode copy 00772 // 00773 _SEH2_TRY 00774 { 00775 // 00776 // If this is our first time, probe the buffer 00777 // 00778 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) 00779 { 00780 // 00781 // Catch a failure here 00782 // 00783 FailedInProbe = TRUE; 00784 00785 // 00786 // Do the probe 00787 // 00788 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); 00789 00790 // 00791 // Passed 00792 // 00793 FailedInProbe = FALSE; 00794 } 00795 00796 // 00797 // Initialize and probe and lock the MDL 00798 // 00799 MmInitializeMdl(Mdl, CurrentAddress, CurrentSize); 00800 MmProbeAndLockPages(Mdl, PreviousMode, IoReadAccess); 00801 PagesLocked = TRUE; 00802 00803 // 00804 // Now map the pages 00805 // 00806 MdlAddress = MmMapLockedPagesSpecifyCache(Mdl, 00807 KernelMode, 00808 MmCached, 00809 NULL, 00810 FALSE, 00811 HighPagePriority); 00812 if (!MdlAddress) 00813 { 00814 // 00815 // Use our SEH handler to pick this up 00816 // 00817 FailedInMapping = TRUE; 00818 ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES); 00819 } 00820 00821 // 00822 // Now let go of the source and grab to the target process 00823 // 00824 KeUnstackDetachProcess(&ApcState); 00825 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); 00826 00827 // 00828 // Check if this is our first time through 00829 // 00830 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) 00831 { 00832 // 00833 // Catch a failure here 00834 // 00835 FailedInProbe = TRUE; 00836 00837 // 00838 // Do the probe 00839 // 00840 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); 00841 00842 // 00843 // Passed 00844 // 00845 FailedInProbe = FALSE; 00846 } 00847 00848 // 00849 // Now do the actual move 00850 // 00851 FailedInMoving = TRUE; 00852 RtlCopyMemory(CurrentTargetAddress, MdlAddress, CurrentSize); 00853 } 00854 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), 00855 &HaveBadAddress, 00856 &BadAddress)) 00857 { 00858 // 00859 // Detach from whoever we may be attached to 00860 // 00861 KeUnstackDetachProcess(&ApcState); 00862 00863 // 00864 // Check if we had mapped the pages 00865 // 00866 if (MdlAddress) MmUnmapLockedPages(MdlAddress, Mdl); 00867 00868 // 00869 // Check if we had locked the pages 00870 // 00871 if (PagesLocked) MmUnlockPages(Mdl); 00872 00873 // 00874 // Check if we hit working set quota 00875 // 00876 if (_SEH2_GetExceptionCode() == STATUS_WORKING_SET_QUOTA) 00877 { 00878 // 00879 // Return the error 00880 // 00881 return STATUS_WORKING_SET_QUOTA; 00882 } 00883 00884 // 00885 // Check if we failed during the probe or mapping 00886 // 00887 if ((FailedInProbe) || (FailedInMapping)) 00888 { 00889 // 00890 // Exit 00891 // 00892 Status = _SEH2_GetExceptionCode(); 00893 _SEH2_YIELD(return Status); 00894 } 00895 00896 // 00897 // Otherwise, we failed probably during the move 00898 // 00899 *ReturnSize = BufferSize - RemainingSize; 00900 if (FailedInMoving) 00901 { 00902 // 00903 // Check if we know exactly where we stopped copying 00904 // 00905 if (HaveBadAddress) 00906 { 00907 // 00908 // Return the exact number of bytes copied 00909 // 00910 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; 00911 } 00912 } 00913 00914 // 00915 // Return partial copy 00916 // 00917 Status = STATUS_PARTIAL_COPY; 00918 } 00919 _SEH2_END; 00920 00921 // 00922 // Check for SEH status 00923 // 00924 if (Status != STATUS_SUCCESS) return Status; 00925 00926 // 00927 // Detach from target 00928 // 00929 KeUnstackDetachProcess(&ApcState); 00930 00931 // 00932 // Unmap and unlock 00933 // 00934 MmUnmapLockedPages(MdlAddress, Mdl); 00935 MmUnlockPages(Mdl); 00936 00937 // 00938 // Update location and size 00939 // 00940 RemainingSize -= CurrentSize; 00941 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); 00942 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + CurrentSize); 00943 } 00944 00945 // 00946 // All bytes read 00947 // 00948 *ReturnSize = BufferSize; 00949 return STATUS_SUCCESS; 00950 } 00951 00952 NTSTATUS 00953 NTAPI 00954 MiDoPoolCopy(IN PEPROCESS SourceProcess, 00955 IN PVOID SourceAddress, 00956 IN PEPROCESS TargetProcess, 00957 OUT PVOID TargetAddress, 00958 IN SIZE_T BufferSize, 00959 IN KPROCESSOR_MODE PreviousMode, 00960 OUT PSIZE_T ReturnSize) 00961 { 00962 UCHAR StackBuffer[MI_POOL_COPY_BYTES]; 00963 SIZE_T TotalSize, CurrentSize, RemainingSize; 00964 volatile BOOLEAN FailedInProbe = FALSE, FailedInMoving, HavePoolAddress = FALSE; 00965 PVOID CurrentAddress = SourceAddress, CurrentTargetAddress = TargetAddress; 00966 PVOID PoolAddress; 00967 KAPC_STATE ApcState; 00968 BOOLEAN HaveBadAddress; 00969 ULONG_PTR BadAddress; 00970 NTSTATUS Status = STATUS_SUCCESS; 00971 PAGED_CODE(); 00972 00973 // 00974 // Calculate the maximum amount of data to move 00975 // 00976 TotalSize = MI_MAX_TRANSFER_SIZE; 00977 if (BufferSize <= MI_MAX_TRANSFER_SIZE) TotalSize = BufferSize; 00978 CurrentSize = TotalSize; 00979 RemainingSize = BufferSize; 00980 00981 // 00982 // Check if we can use the stack 00983 // 00984 if (BufferSize <= MI_POOL_COPY_BYTES) 00985 { 00986 // 00987 // Use it 00988 // 00989 PoolAddress = (PVOID)StackBuffer; 00990 } 00991 else 00992 { 00993 // 00994 // Allocate pool 00995 // 00996 PoolAddress = ExAllocatePoolWithTag(NonPagedPool, TotalSize, 'VmRw'); 00997 if (!PoolAddress) ASSERT(FALSE); 00998 HavePoolAddress = TRUE; 00999 } 01000 01001 // 01002 // Loop as long as there is still data 01003 // 01004 while (RemainingSize > 0) 01005 { 01006 // 01007 // Check if this transfer will finish everything off 01008 // 01009 if (RemainingSize < CurrentSize) CurrentSize = RemainingSize; 01010 01011 // 01012 // Attach to the source address space 01013 // 01014 KeStackAttachProcess(&SourceProcess->Pcb, &ApcState); 01015 01016 // 01017 // Reset state for this pass 01018 // 01019 FailedInMoving = FALSE; 01020 ASSERT(FailedInProbe == FALSE); 01021 01022 // 01023 // Protect user-mode copy 01024 // 01025 _SEH2_TRY 01026 { 01027 // 01028 // If this is our first time, probe the buffer 01029 // 01030 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) 01031 { 01032 // 01033 // Catch a failure here 01034 // 01035 FailedInProbe = TRUE; 01036 01037 // 01038 // Do the probe 01039 // 01040 ProbeForRead(SourceAddress, BufferSize, sizeof(CHAR)); 01041 01042 // 01043 // Passed 01044 // 01045 FailedInProbe = FALSE; 01046 } 01047 01048 // 01049 // Do the copy 01050 // 01051 RtlCopyMemory(PoolAddress, CurrentAddress, CurrentSize); 01052 01053 // 01054 // Now let go of the source and grab to the target process 01055 // 01056 KeUnstackDetachProcess(&ApcState); 01057 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); 01058 01059 // 01060 // Check if this is our first time through 01061 // 01062 if ((CurrentAddress == SourceAddress) && (PreviousMode != KernelMode)) 01063 { 01064 // 01065 // Catch a failure here 01066 // 01067 FailedInProbe = TRUE; 01068 01069 // 01070 // Do the probe 01071 // 01072 ProbeForWrite(TargetAddress, BufferSize, sizeof(CHAR)); 01073 01074 // 01075 // Passed 01076 // 01077 FailedInProbe = FALSE; 01078 } 01079 01080 // 01081 // Now do the actual move 01082 // 01083 FailedInMoving = TRUE; 01084 RtlCopyMemory(CurrentTargetAddress, PoolAddress, CurrentSize); 01085 } 01086 _SEH2_EXCEPT(MiGetExceptionInfo(_SEH2_GetExceptionInformation(), 01087 &HaveBadAddress, 01088 &BadAddress)) 01089 { 01090 // 01091 // Detach from whoever we may be attached to 01092 // 01093 KeUnstackDetachProcess(&ApcState); 01094 01095 // 01096 // Check if we had allocated pool 01097 // 01098 if (HavePoolAddress) ExFreePool(PoolAddress); 01099 01100 // 01101 // Check if we failed during the probe 01102 // 01103 if (FailedInProbe) 01104 { 01105 // 01106 // Exit 01107 // 01108 Status = _SEH2_GetExceptionCode(); 01109 _SEH2_YIELD(return Status); 01110 } 01111 01112 // 01113 // Otherwise, we failed, probably during the move 01114 // 01115 *ReturnSize = BufferSize - RemainingSize; 01116 if (FailedInMoving) 01117 { 01118 // 01119 // Check if we know exactly where we stopped copying 01120 // 01121 if (HaveBadAddress) 01122 { 01123 // 01124 // Return the exact number of bytes copied 01125 // 01126 *ReturnSize = BadAddress - (ULONG_PTR)SourceAddress; 01127 } 01128 } 01129 01130 // 01131 // Return partial copy 01132 // 01133 Status = STATUS_PARTIAL_COPY; 01134 } 01135 _SEH2_END; 01136 01137 // 01138 // Check for SEH status 01139 // 01140 if (Status != STATUS_SUCCESS) return Status; 01141 01142 // 01143 // Detach from target 01144 // 01145 KeUnstackDetachProcess(&ApcState); 01146 01147 // 01148 // Update location and size 01149 // 01150 RemainingSize -= CurrentSize; 01151 CurrentAddress = (PVOID)((ULONG_PTR)CurrentAddress + CurrentSize); 01152 CurrentTargetAddress = (PVOID)((ULONG_PTR)CurrentTargetAddress + 01153 CurrentSize); 01154 } 01155 01156 // 01157 // Check if we had allocated pool 01158 // 01159 if (HavePoolAddress) ExFreePool(PoolAddress); 01160 01161 // 01162 // All bytes read 01163 // 01164 *ReturnSize = BufferSize; 01165 return STATUS_SUCCESS; 01166 } 01167 01168 NTSTATUS 01169 NTAPI 01170 MmCopyVirtualMemory(IN PEPROCESS SourceProcess, 01171 IN PVOID SourceAddress, 01172 IN PEPROCESS TargetProcess, 01173 OUT PVOID TargetAddress, 01174 IN SIZE_T BufferSize, 01175 IN KPROCESSOR_MODE PreviousMode, 01176 OUT PSIZE_T ReturnSize) 01177 { 01178 NTSTATUS Status; 01179 PEPROCESS Process = SourceProcess; 01180 01181 // 01182 // Don't accept zero-sized buffers 01183 // 01184 if (!BufferSize) return STATUS_SUCCESS; 01185 01186 // 01187 // If we are copying from ourselves, lock the target instead 01188 // 01189 if (SourceProcess == PsGetCurrentProcess()) Process = TargetProcess; 01190 01191 // 01192 // Acquire rundown protection 01193 // 01194 if (!ExAcquireRundownProtection(&Process->RundownProtect)) 01195 { 01196 // 01197 // Fail 01198 // 01199 return STATUS_PROCESS_IS_TERMINATING; 01200 } 01201 01202 // 01203 // See if we should use the pool copy 01204 // 01205 if (BufferSize > MI_POOL_COPY_BYTES) 01206 { 01207 // 01208 // Use MDL-copy 01209 // 01210 Status = MiDoMappedCopy(SourceProcess, 01211 SourceAddress, 01212 TargetProcess, 01213 TargetAddress, 01214 BufferSize, 01215 PreviousMode, 01216 ReturnSize); 01217 } 01218 else 01219 { 01220 // 01221 // Do pool copy 01222 // 01223 Status = MiDoPoolCopy(SourceProcess, 01224 SourceAddress, 01225 TargetProcess, 01226 TargetAddress, 01227 BufferSize, 01228 PreviousMode, 01229 ReturnSize); 01230 } 01231 01232 // 01233 // Release the lock 01234 // 01235 ExReleaseRundownProtection(&Process->RundownProtect); 01236 return Status; 01237 } 01238 01239 NTSTATUS 01240 NTAPI 01241 MmFlushVirtualMemory(IN PEPROCESS Process, 01242 IN OUT PVOID *BaseAddress, 01243 IN OUT PSIZE_T RegionSize, 01244 OUT PIO_STATUS_BLOCK IoStatusBlock) 01245 { 01246 PAGED_CODE(); 01247 UNIMPLEMENTED; 01248 01249 // 01250 // Fake success 01251 // 01252 return STATUS_SUCCESS; 01253 } 01254 01255 ULONG 01256 NTAPI 01257 MiGetPageProtection(IN PMMPTE PointerPte) 01258 { 01259 MMPTE TempPte; 01260 PMMPFN Pfn; 01261 PAGED_CODE(); 01262 01263 /* Copy this PTE's contents */ 01264 TempPte = *PointerPte; 01265 01266 /* Assure it's not totally zero */ 01267 ASSERT(TempPte.u.Long); 01268 01269 /* Check for a special prototype format */ 01270 if (TempPte.u.Soft.Valid == 0 && 01271 TempPte.u.Soft.Prototype == 1) 01272 { 01273 /* Unsupported now */ 01274 UNIMPLEMENTED; 01275 ASSERT(FALSE); 01276 } 01277 01278 /* In the easy case of transition or demand zero PTE just return its protection */ 01279 if (!TempPte.u.Hard.Valid) return MmProtectToValue[TempPte.u.Soft.Protection]; 01280 01281 /* If we get here, the PTE is valid, so look up the page in PFN database */ 01282 Pfn = MiGetPfnEntry(TempPte.u.Hard.PageFrameNumber); 01283 if (!Pfn->u3.e1.PrototypePte) 01284 { 01285 /* Return protection of the original pte */ 01286 ASSERT(Pfn->u4.AweAllocation == 0); 01287 return MmProtectToValue[Pfn->OriginalPte.u.Soft.Protection]; 01288 } 01289 01290 /* This is software PTE */ 01291 DPRINT1("Prototype PTE: %lx %p\n", TempPte.u.Hard.PageFrameNumber, Pfn); 01292 DPRINT1("VA: %p\n", MiPteToAddress(&TempPte)); 01293 DPRINT1("Mask: %lx\n", TempPte.u.Soft.Protection); 01294 DPRINT1("Mask2: %lx\n", Pfn->OriginalPte.u.Soft.Protection); 01295 return MmProtectToValue[TempPte.u.Soft.Protection]; 01296 } 01297 01298 ULONG 01299 NTAPI 01300 MiQueryAddressState(IN PVOID Va, 01301 IN PMMVAD Vad, 01302 IN PEPROCESS TargetProcess, 01303 OUT PULONG ReturnedProtect, 01304 OUT PVOID *NextVa) 01305 { 01306 01307 PMMPTE PointerPte; 01308 PMMPDE PointerPde; 01309 MMPTE TempPte; 01310 BOOLEAN DemandZeroPte = TRUE, ValidPte = FALSE; 01311 ULONG State = MEM_RESERVE, Protect = 0; 01312 ASSERT((Vad->StartingVpn <= ((ULONG_PTR)Va >> PAGE_SHIFT)) && 01313 (Vad->EndingVpn >= ((ULONG_PTR)Va >> PAGE_SHIFT))); 01314 01315 /* Only normal VADs supported */ 01316 ASSERT(Vad->u.VadFlags.VadType == VadNone); 01317 01318 /* Get the PDE and PTE for the address */ 01319 PointerPde = MiAddressToPde(Va); 01320 PointerPte = MiAddressToPte(Va); 01321 01322 /* Return the next range */ 01323 *NextVa = (PVOID)((ULONG_PTR)Va + PAGE_SIZE); 01324 01325 /* Is the PDE demand-zero? */ 01326 if (PointerPde->u.Long != 0) 01327 { 01328 /* It is not. Is it valid? */ 01329 if (PointerPde->u.Hard.Valid == 0) 01330 { 01331 /* Is isn't, fault it in */ 01332 PointerPte = MiPteToAddress(PointerPde); 01333 MiMakeSystemAddressValid(PointerPte, TargetProcess); 01334 ValidPte = TRUE; 01335 } 01336 } 01337 else 01338 { 01339 /* It is, skip it and move to the next PDE */ 01340 *NextVa = MiPdeToAddress(PointerPde + 1); 01341 } 01342 01343 /* Is it safe to try reading the PTE? */ 01344 if (ValidPte) 01345 { 01346 /* FIXME: watch out for large pages */ 01347 01348 /* Capture the PTE */ 01349 TempPte = *PointerPte; 01350 if (TempPte.u.Long != 0) 01351 { 01352 /* The PTE is valid, so it's not zeroed out */ 01353 DemandZeroPte = FALSE; 01354 01355 /* Is it a decommited, invalid, or faulted PTE? */ 01356 if ((TempPte.u.Soft.Protection == MM_DECOMMIT) && 01357 (TempPte.u.Hard.Valid == 0) && 01358 ((TempPte.u.Soft.Prototype == 0) || 01359 (TempPte.u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED))) 01360 { 01361 /* Otherwise our defaults should hold */ 01362 ASSERT(Protect == 0); 01363 ASSERT(State == MEM_RESERVE); 01364 } 01365 else 01366 { 01367 /* This means it's committed */ 01368 State = MEM_COMMIT; 01369 01370 /* Get protection state of this page */ 01371 Protect = MiGetPageProtection(PointerPte); 01372 01373 /* Check if this is an image-backed VAD */ 01374 if ((TempPte.u.Soft.Valid == 0) && 01375 (TempPte.u.Soft.Prototype == 1) && 01376 (Vad->u.VadFlags.PrivateMemory == 0) && 01377 (Vad->ControlArea)) 01378 { 01379 DPRINT1("Not supported\n"); 01380 ASSERT(FALSE); 01381 } 01382 } 01383 } 01384 } 01385 01386 /* Check if this was a demand-zero PTE, since we need to find the state */ 01387 if (DemandZeroPte) 01388 { 01389 /* Check if this is private commited memory, or an image-backed VAD */ 01390 if ((Vad->u.VadFlags.PrivateMemory == 0) && (Vad->ControlArea)) 01391 { 01392 DPRINT1("Not supported\n"); 01393 ASSERT(FALSE); 01394 } 01395 else if (Vad->u.VadFlags.MemCommit) 01396 { 01397 /* This is committed memory */ 01398 State = MEM_COMMIT; 01399 01400 /* Convert the protection */ 01401 Protect = MmProtectToValue[Vad->u.VadFlags.Protection]; 01402 } 01403 } 01404 01405 /* Return the protection code */ 01406 *ReturnedProtect = Protect; 01407 return State; 01408 } 01409 01410 NTSTATUS 01411 NTAPI 01412 MiQueryMemoryBasicInformation(IN HANDLE ProcessHandle, 01413 IN PVOID BaseAddress, 01414 OUT PVOID MemoryInformation, 01415 IN SIZE_T MemoryInformationLength, 01416 OUT PSIZE_T ReturnLength) 01417 { 01418 PEPROCESS TargetProcess; 01419 NTSTATUS Status = STATUS_SUCCESS; 01420 PMMVAD Vad = NULL; 01421 PVOID Address, NextAddress; 01422 BOOLEAN Found = FALSE; 01423 ULONG NewProtect, NewState; 01424 ULONG_PTR BaseVpn; 01425 MEMORY_BASIC_INFORMATION MemoryInfo; 01426 KAPC_STATE ApcState; 01427 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 01428 PMEMORY_AREA MemoryArea; 01429 SIZE_T ResultLength; 01430 01431 /* Check for illegal addresses in user-space, or the shared memory area */ 01432 if ((BaseAddress > MM_HIGHEST_VAD_ADDRESS) || 01433 (PAGE_ALIGN(BaseAddress) == (PVOID)MM_SHARED_USER_DATA_VA)) 01434 { 01435 Address = PAGE_ALIGN(BaseAddress); 01436 01437 /* Make up an info structure describing this range */ 01438 MemoryInfo.BaseAddress = Address; 01439 MemoryInfo.AllocationProtect = PAGE_READONLY; 01440 MemoryInfo.Type = MEM_PRIVATE; 01441 01442 /* Special case for shared data */ 01443 if (Address == (PVOID)MM_SHARED_USER_DATA_VA) 01444 { 01445 MemoryInfo.AllocationBase = (PVOID)MM_SHARED_USER_DATA_VA; 01446 MemoryInfo.State = MEM_COMMIT; 01447 MemoryInfo.Protect = PAGE_READONLY; 01448 MemoryInfo.RegionSize = PAGE_SIZE; 01449 } 01450 else 01451 { 01452 MemoryInfo.AllocationBase = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1; 01453 MemoryInfo.State = MEM_RESERVE; 01454 MemoryInfo.Protect = PAGE_NOACCESS; 01455 MemoryInfo.RegionSize = (ULONG_PTR)MM_HIGHEST_USER_ADDRESS + 1 - (ULONG_PTR)Address; 01456 } 01457 01458 /* Return the data, NtQueryInformation already probed it*/ 01459 if (PreviousMode != KernelMode) 01460 { 01461 _SEH2_TRY 01462 { 01463 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 01464 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 01465 } 01466 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 01467 { 01468 Status = _SEH2_GetExceptionCode(); 01469 } 01470 _SEH2_END; 01471 } 01472 else 01473 { 01474 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 01475 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 01476 } 01477 01478 return Status; 01479 } 01480 01481 /* Check if this is for a local or remote process */ 01482 if (ProcessHandle == NtCurrentProcess()) 01483 { 01484 TargetProcess = PsGetCurrentProcess(); 01485 } 01486 else 01487 { 01488 /* Reference the target process */ 01489 Status = ObReferenceObjectByHandle(ProcessHandle, 01490 PROCESS_QUERY_INFORMATION, 01491 PsProcessType, 01492 ExGetPreviousMode(), 01493 (PVOID*)&TargetProcess, 01494 NULL); 01495 if (!NT_SUCCESS(Status)) return Status; 01496 01497 /* Attach to it now */ 01498 KeStackAttachProcess(&TargetProcess->Pcb, &ApcState); 01499 } 01500 01501 /* Loop the VADs */ 01502 ASSERT(TargetProcess->VadRoot.NumberGenericTableElements); 01503 if (TargetProcess->VadRoot.NumberGenericTableElements) 01504 { 01505 /* Scan on the right */ 01506 Vad = (PMMVAD)TargetProcess->VadRoot.BalancedRoot.RightChild; 01507 BaseVpn = (ULONG_PTR)BaseAddress >> PAGE_SHIFT; 01508 while (Vad) 01509 { 01510 /* Check if this VAD covers the allocation range */ 01511 if ((BaseVpn >= Vad->StartingVpn) && 01512 (BaseVpn <= Vad->EndingVpn)) 01513 { 01514 /* We're done */ 01515 Found = TRUE; 01516 break; 01517 } 01518 01519 /* Check if this VAD is too high */ 01520 if (BaseVpn < Vad->StartingVpn) 01521 { 01522 /* Stop if there is no left child */ 01523 if (!Vad->LeftChild) break; 01524 01525 /* Search on the left next */ 01526 Vad = Vad->LeftChild; 01527 } 01528 else 01529 { 01530 /* Then this VAD is too low, keep searching on the right */ 01531 ASSERT(BaseVpn > Vad->EndingVpn); 01532 01533 /* Stop if there is no right child */ 01534 if (!Vad->RightChild) break; 01535 01536 /* Search on the right next */ 01537 Vad = Vad->RightChild; 01538 } 01539 } 01540 } 01541 01542 /* Was a VAD found? */ 01543 if (!Found) 01544 { 01545 Address = PAGE_ALIGN(BaseAddress); 01546 01547 /* Calculate region size */ 01548 if (Vad) 01549 { 01550 if (Vad->StartingVpn >= BaseVpn) 01551 { 01552 /* Region size is the free space till the start of that VAD */ 01553 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address; 01554 } 01555 else 01556 { 01557 /* Get the next VAD */ 01558 Vad = (PMMVAD)MiGetNextNode((PMMADDRESS_NODE)Vad); 01559 if (Vad) 01560 { 01561 /* Region size is the free space till the start of that VAD */ 01562 MemoryInfo.RegionSize = (ULONG_PTR)(Vad->StartingVpn << PAGE_SHIFT) - (ULONG_PTR)Address; 01563 } 01564 else 01565 { 01566 /* Maximum possible region size with that base address */ 01567 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address; 01568 } 01569 } 01570 } 01571 else 01572 { 01573 /* Maximum possible region size with that base address */ 01574 MemoryInfo.RegionSize = (PCHAR)MM_HIGHEST_VAD_ADDRESS + 1 - (PCHAR)Address; 01575 } 01576 01577 /* Check if we were attached */ 01578 if (ProcessHandle != NtCurrentProcess()) 01579 { 01580 /* Detach and derefernece the process */ 01581 KeUnstackDetachProcess(&ApcState); 01582 ObDereferenceObject(TargetProcess); 01583 } 01584 01585 /* Build the rest of the initial information block */ 01586 MemoryInfo.BaseAddress = Address; 01587 MemoryInfo.AllocationBase = NULL; 01588 MemoryInfo.AllocationProtect = 0; 01589 MemoryInfo.State = MEM_FREE; 01590 MemoryInfo.Protect = PAGE_NOACCESS; 01591 MemoryInfo.Type = 0; 01592 01593 /* Return the data, NtQueryInformation already probed it*/ 01594 if (PreviousMode != KernelMode) 01595 { 01596 _SEH2_TRY 01597 { 01598 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 01599 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 01600 } 01601 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 01602 { 01603 Status = _SEH2_GetExceptionCode(); 01604 } 01605 _SEH2_END; 01606 } 01607 else 01608 { 01609 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 01610 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 01611 } 01612 01613 return Status; 01614 } 01615 01616 /* Set the correct memory type based on what kind of VAD this is */ 01617 if ((Vad->u.VadFlags.PrivateMemory) || 01618 (Vad->u.VadFlags.VadType == VadRotatePhysical)) 01619 { 01620 MemoryInfo.Type = MEM_PRIVATE; 01621 } 01622 else if (Vad->u.VadFlags.VadType == VadImageMap) 01623 { 01624 MemoryInfo.Type = MEM_IMAGE; 01625 } 01626 else 01627 { 01628 MemoryInfo.Type = MEM_MAPPED; 01629 } 01630 01631 /* Lock the address space of the process */ 01632 MmLockAddressSpace(&TargetProcess->Vm); 01633 01634 /* Find the memory area the specified address belongs to */ 01635 MemoryArea = MmLocateMemoryAreaByAddress(&TargetProcess->Vm, BaseAddress); 01636 ASSERT(MemoryArea != NULL); 01637 01638 /* Determine information dependent on the memory area type */ 01639 if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW) 01640 { 01641 Status = MmQuerySectionView(MemoryArea, BaseAddress, &MemoryInfo, &ResultLength); 01642 ASSERT(NT_SUCCESS(Status)); 01643 } 01644 else 01645 { 01646 /* Build the initial information block */ 01647 Address = PAGE_ALIGN(BaseAddress); 01648 MemoryInfo.BaseAddress = Address; 01649 MemoryInfo.AllocationBase = (PVOID)(Vad->StartingVpn << PAGE_SHIFT); 01650 MemoryInfo.AllocationProtect = MmProtectToValue[Vad->u.VadFlags.Protection]; 01651 MemoryInfo.Type = MEM_PRIVATE; 01652 01653 /* Find the largest chunk of memory which has the same state and protection mask */ 01654 MemoryInfo.State = MiQueryAddressState(Address, 01655 Vad, 01656 TargetProcess, 01657 &MemoryInfo.Protect, 01658 &NextAddress); 01659 Address = NextAddress; 01660 while (((ULONG_PTR)Address >> PAGE_SHIFT) <= Vad->EndingVpn) 01661 { 01662 /* Keep going unless the state or protection mask changed */ 01663 NewState = MiQueryAddressState(Address, Vad, TargetProcess, &NewProtect, &NextAddress); 01664 if ((NewState != MemoryInfo.State) || (NewProtect != MemoryInfo.Protect)) break; 01665 Address = NextAddress; 01666 } 01667 01668 /* Now that we know the last VA address, calculate the region size */ 01669 MemoryInfo.RegionSize = ((ULONG_PTR)Address - (ULONG_PTR)MemoryInfo.BaseAddress); 01670 } 01671 01672 /* Unlock the address space of the process */ 01673 MmUnlockAddressSpace(&TargetProcess->Vm); 01674 01675 /* Check if we were attached */ 01676 if (ProcessHandle != NtCurrentProcess()) 01677 { 01678 /* Detach and derefernece the process */ 01679 KeUnstackDetachProcess(&ApcState); 01680 ObDereferenceObject(TargetProcess); 01681 } 01682 01683 /* Return the data, NtQueryInformation already probed it*/ 01684 if (PreviousMode != KernelMode) 01685 { 01686 _SEH2_TRY 01687 { 01688 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 01689 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 01690 } 01691 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 01692 { 01693 Status = _SEH2_GetExceptionCode(); 01694 } 01695 _SEH2_END; 01696 } 01697 else 01698 { 01699 *(PMEMORY_BASIC_INFORMATION)MemoryInformation = MemoryInfo; 01700 if (ReturnLength) *ReturnLength = sizeof(MEMORY_BASIC_INFORMATION); 01701 } 01702 01703 /* All went well */ 01704 DPRINT("Base: %p AllocBase: %p AllocProtect: %lx Protect: %lx " 01705 "State: %lx Type: %lx Size: %lx\n", 01706 MemoryInfo.BaseAddress, MemoryInfo.AllocationBase, 01707 MemoryInfo.AllocationProtect, MemoryInfo.Protect, 01708 MemoryInfo.State, MemoryInfo.Type, MemoryInfo.RegionSize); 01709 01710 return Status; 01711 } 01712 01713 NTSTATUS 01714 NTAPI 01715 MiProtectVirtualMemory(IN PEPROCESS Process, 01716 IN OUT PVOID *BaseAddress, 01717 IN OUT PSIZE_T NumberOfBytesToProtect, 01718 IN ULONG NewAccessProtection, 01719 OUT PULONG OldAccessProtection OPTIONAL) 01720 { 01721 PMEMORY_AREA MemoryArea; 01722 01723 MemoryArea = MmLocateMemoryAreaByAddress(&Process->Vm, *BaseAddress); 01724 if ((MemoryArea) && (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)) 01725 { 01726 return MiRosProtectVirtualMemory(Process, 01727 BaseAddress, 01728 NumberOfBytesToProtect, 01729 NewAccessProtection, 01730 OldAccessProtection); 01731 } 01732 01733 UNIMPLEMENTED; 01734 return STATUS_CONFLICTING_ADDRESSES; 01735 } 01736 01737 VOID 01738 NTAPI 01739 MiMakePdeExistAndMakeValid(IN PMMPTE PointerPde, 01740 IN PEPROCESS TargetProcess, 01741 IN KIRQL OldIrql) 01742 { 01743 PMMPTE PointerPte, PointerPpe, PointerPxe; 01744 01745 // 01746 // Sanity checks. The latter is because we only use this function with the 01747 // PFN lock not held, so it may go away in the future. 01748 // 01749 ASSERT(KeAreAllApcsDisabled() == TRUE); 01750 ASSERT(OldIrql == MM_NOIRQL); 01751 01752 // 01753 // Also get the PPE and PXE. This is okay not to #ifdef because they will 01754 // return the same address as the PDE on 2-level page table systems. 01755 // 01756 // If everything is already valid, there is nothing to do. 01757 // 01758 PointerPpe = MiAddressToPte(PointerPde); 01759 PointerPxe = MiAddressToPde(PointerPde); 01760 if ((PointerPxe->u.Hard.Valid) && 01761 (PointerPpe->u.Hard.Valid) && 01762 (PointerPde->u.Hard.Valid)) 01763 { 01764 return; 01765 } 01766 01767 // 01768 // At least something is invalid, so begin by getting the PTE for the PDE itself 01769 // and then lookup each additional level. We must do it in this precise order 01770 // because the pagfault.c code (as well as in Windows) depends that the next 01771 // level up (higher) must be valid when faulting a lower level 01772 // 01773 PointerPte = MiPteToAddress(PointerPde); 01774 do 01775 { 01776 // 01777 // Make sure APCs continued to be disabled 01778 // 01779 ASSERT(KeAreAllApcsDisabled() == TRUE); 01780 01781 // 01782 // First, make the PXE valid if needed 01783 // 01784 if (!PointerPxe->u.Hard.Valid) 01785 { 01786 MiMakeSystemAddressValid(PointerPpe, TargetProcess); 01787 ASSERT(PointerPxe->u.Hard.Valid == 1); 01788 } 01789 01790 // 01791 // Next, the PPE 01792 // 01793 if (!PointerPpe->u.Hard.Valid) 01794 { 01795 MiMakeSystemAddressValid(PointerPde, TargetProcess); 01796 ASSERT(PointerPpe->u.Hard.Valid == 1); 01797 } 01798 01799 // 01800 // And finally, make the PDE itself valid. 01801 // 01802 MiMakeSystemAddressValid(PointerPte, TargetProcess); 01803 01804 // 01805 // This should've worked the first time so the loop is really just for 01806 // show -- ASSERT that we're actually NOT going to be looping. 01807 // 01808 ASSERT(PointerPxe->u.Hard.Valid == 1); 01809 ASSERT(PointerPpe->u.Hard.Valid == 1); 01810 ASSERT(PointerPde->u.Hard.Valid == 1); 01811 } while (!(PointerPxe->u.Hard.Valid) || 01812 !(PointerPpe->u.Hard.Valid) || 01813 !(PointerPde->u.Hard.Valid)); 01814 } 01815 01816 VOID 01817 NTAPI 01818 MiProcessValidPteList(IN PMMPTE *ValidPteList, 01819 IN ULONG Count) 01820 { 01821 KIRQL OldIrql; 01822 ULONG i; 01823 MMPTE TempPte; 01824 PFN_NUMBER PageFrameIndex; 01825 PMMPFN Pfn1, Pfn2; 01826 01827 // 01828 // Acquire the PFN lock and loop all the PTEs in the list 01829 // 01830 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock); 01831 for (i = 0; i != Count; i++) 01832 { 01833 // 01834 // The PTE must currently be valid 01835 // 01836 TempPte = *ValidPteList[i]; 01837 ASSERT(TempPte.u.Hard.Valid == 1); 01838 01839 // 01840 // Get the PFN entry for the page itself, and then for its page table 01841 // 01842 PageFrameIndex = PFN_FROM_PTE(&TempPte); 01843 Pfn1 = MiGetPfnEntry(PageFrameIndex); 01844 Pfn2 = MiGetPfnEntry(Pfn1->u4.PteFrame); 01845 01846 // 01847 // Decrement the share count on the page table, and then on the page 01848 // itself 01849 // 01850 MiDecrementShareCount(Pfn2, Pfn1->u4.PteFrame); 01851 MI_SET_PFN_DELETED(Pfn1); 01852 MiDecrementShareCount(Pfn1, PageFrameIndex); 01853 01854 // 01855 // Make the page decommitted 01856 // 01857 MI_WRITE_INVALID_PTE(ValidPteList[i], MmDecommittedPte); 01858 } 01859 01860 // 01861 // All the PTEs have been dereferenced and made invalid, flush the TLB now 01862 // and then release the PFN lock 01863 // 01864 KeFlushCurrentTb(); 01865 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql); 01866 } 01867 01868 ULONG 01869 NTAPI 01870 MiDecommitPages(IN PVOID StartingAddress, 01871 IN PMMPTE EndingPte, 01872 IN PEPROCESS Process, 01873 IN PMMVAD Vad) 01874 { 01875 PMMPTE PointerPde, PointerPte, CommitPte = NULL; 01876 ULONG CommitReduction = 0; 01877 PMMPTE ValidPteList[256]; 01878 ULONG PteCount = 0; 01879 PMMPFN Pfn1; 01880 MMPTE PteContents; 01881 PUSHORT UsedPageTableEntries; 01882 PETHREAD CurrentThread = PsGetCurrentThread(); 01883 01884 // 01885 // Get the PTE and PTE for the address, and lock the working set 01886 // If this was a VAD for a MEM_COMMIT allocation, also figure out where the 01887 // commited range ends so that we can do the right accounting. 01888 // 01889 PointerPde = MiAddressToPde(StartingAddress); 01890 PointerPte = MiAddressToPte(StartingAddress); 01891 if (Vad->u.VadFlags.MemCommit) CommitPte = MiAddressToPte(Vad->EndingVpn << PAGE_SHIFT); 01892 MiLockWorkingSet(CurrentThread, &Process->Vm); 01893 01894 // 01895 // Make the PDE valid, and now loop through each page's worth of data 01896 // 01897 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 01898 while (PointerPte <= EndingPte) 01899 { 01900 // 01901 // Check if we've crossed a PDE boundary 01902 // 01903 if ((((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1)) == 0) 01904 { 01905 // 01906 // Get the new PDE and flush the valid PTEs we had built up until 01907 // now. This helps reduce the amount of TLB flushing we have to do. 01908 // Note that Windows does a much better job using timestamps and 01909 // such, and does not flush the entire TLB all the time, but right 01910 // now we have bigger problems to worry about than TLB flushing. 01911 // 01912 PointerPde = MiAddressToPde(StartingAddress); 01913 if (PteCount) 01914 { 01915 MiProcessValidPteList(ValidPteList, PteCount); 01916 PteCount = 0; 01917 } 01918 01919 // 01920 // Make this PDE valid 01921 // 01922 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 01923 } 01924 01925 // 01926 // Read this PTE. It might be active or still demand-zero. 01927 // 01928 PteContents = *PointerPte; 01929 if (PteContents.u.Long) 01930 { 01931 // 01932 // The PTE is active. It might be valid and in a working set, or 01933 // it might be a prototype PTE or paged out or even in transition. 01934 // 01935 if (PointerPte->u.Long == MmDecommittedPte.u.Long) 01936 { 01937 // 01938 // It's already decommited, so there's nothing for us to do here 01939 // 01940 CommitReduction++; 01941 } 01942 else 01943 { 01944 // 01945 // Remove it from the counters, and check if it was valid or not 01946 // 01947 //Process->NumberOfPrivatePages--; 01948 if (PteContents.u.Hard.Valid) 01949 { 01950 // 01951 // It's valid. At this point make sure that it is not a ROS 01952 // PFN. Also, we don't support ProtoPTEs in this code path. 01953 // 01954 Pfn1 = MiGetPfnEntry(PteContents.u.Hard.PageFrameNumber); 01955 ASSERT(MI_IS_ROS_PFN(Pfn1) == FALSE); 01956 ASSERT(Pfn1->u3.e1.PrototypePte == FALSE); 01957 01958 // 01959 // Flush any pending PTEs that we had not yet flushed, if our 01960 // list has gotten too big, then add this PTE to the flush list. 01961 // 01962 if (PteCount == 256) 01963 { 01964 MiProcessValidPteList(ValidPteList, PteCount); 01965 PteCount = 0; 01966 } 01967 ValidPteList[PteCount++] = PointerPte; 01968 } 01969 else 01970 { 01971 // 01972 // We do not support any of these other scenarios at the moment 01973 // 01974 ASSERT(PteContents.u.Soft.Prototype == 0); 01975 ASSERT(PteContents.u.Soft.Transition == 0); 01976 ASSERT(PteContents.u.Soft.PageFileHigh == 0); 01977 01978 // 01979 // So the only other possibility is that it is still a demand 01980 // zero PTE, in which case we undo the accounting we did 01981 // earlier and simply make the page decommitted. 01982 // 01983 //Process->NumberOfPrivatePages++; 01984 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 01985 } 01986 } 01987 } 01988 else 01989 { 01990 // 01991 // This used to be a zero PTE and it no longer is, so we must add a 01992 // reference to the pagetable. 01993 // 01994 UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(StartingAddress)]; 01995 (*UsedPageTableEntries)++; 01996 ASSERT((*UsedPageTableEntries) <= PTE_COUNT); 01997 01998 // 01999 // Next, we account for decommitted PTEs and make the PTE as such 02000 // 02001 if (PointerPte > CommitPte) CommitReduction++; 02002 MI_WRITE_INVALID_PTE(PointerPte, MmDecommittedPte); 02003 } 02004 02005 // 02006 // Move to the next PTE and the next address 02007 // 02008 PointerPte++; 02009 StartingAddress = (PVOID)((ULONG_PTR)StartingAddress + PAGE_SIZE); 02010 } 02011 02012 // 02013 // Flush any dangling PTEs from the loop in the last page table, and then 02014 // release the working set and return the commit reduction accounting. 02015 // 02016 if (PteCount) MiProcessValidPteList(ValidPteList, PteCount); 02017 MiUnlockWorkingSet(CurrentThread, &Process->Vm); 02018 return CommitReduction; 02019 } 02020 02021 /* PUBLIC FUNCTIONS ***********************************************************/ 02022 02023 /* 02024 * @unimplemented 02025 */ 02026 PVOID 02027 NTAPI 02028 MmGetVirtualForPhysical(IN PHYSICAL_ADDRESS PhysicalAddress) 02029 { 02030 UNIMPLEMENTED; 02031 return 0; 02032 } 02033 02034 /* 02035 * @unimplemented 02036 */ 02037 PVOID 02038 NTAPI 02039 MmSecureVirtualMemory(IN PVOID Address, 02040 IN SIZE_T Length, 02041 IN ULONG Mode) 02042 { 02043 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED; 02044 return Address; 02045 } 02046 02047 /* 02048 * @unimplemented 02049 */ 02050 VOID 02051 NTAPI 02052 MmUnsecureVirtualMemory(IN PVOID SecureMem) 02053 { 02054 static BOOLEAN Warn; if (!Warn++) UNIMPLEMENTED; 02055 } 02056 02057 /* SYSTEM CALLS ***************************************************************/ 02058 02059 NTSTATUS 02060 NTAPI 02061 NtReadVirtualMemory(IN HANDLE ProcessHandle, 02062 IN PVOID BaseAddress, 02063 OUT PVOID Buffer, 02064 IN SIZE_T NumberOfBytesToRead, 02065 OUT PSIZE_T NumberOfBytesRead OPTIONAL) 02066 { 02067 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 02068 PEPROCESS Process; 02069 NTSTATUS Status = STATUS_SUCCESS; 02070 SIZE_T BytesRead = 0; 02071 PAGED_CODE(); 02072 02073 // 02074 // Check if we came from user mode 02075 // 02076 if (PreviousMode != KernelMode) 02077 { 02078 // 02079 // Validate the read addresses 02080 // 02081 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToRead) < (ULONG_PTR)BaseAddress) || 02082 (((ULONG_PTR)Buffer + NumberOfBytesToRead) < (ULONG_PTR)Buffer) || 02083 (((ULONG_PTR)BaseAddress + NumberOfBytesToRead) > MmUserProbeAddress) || 02084 (((ULONG_PTR)Buffer + NumberOfBytesToRead) > MmUserProbeAddress)) 02085 { 02086 // 02087 // Don't allow to write into kernel space 02088 // 02089 return STATUS_ACCESS_VIOLATION; 02090 } 02091 02092 // 02093 // Enter SEH for probe 02094 // 02095 _SEH2_TRY 02096 { 02097 // 02098 // Probe the output value 02099 // 02100 if (NumberOfBytesRead) ProbeForWriteSize_t(NumberOfBytesRead); 02101 } 02102 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02103 { 02104 // 02105 // Get exception code 02106 // 02107 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02108 } 02109 _SEH2_END; 02110 } 02111 02112 // 02113 // Don't do zero-byte transfers 02114 // 02115 if (NumberOfBytesToRead) 02116 { 02117 // 02118 // Reference the process 02119 // 02120 Status = ObReferenceObjectByHandle(ProcessHandle, 02121 PROCESS_VM_READ, 02122 PsProcessType, 02123 PreviousMode, 02124 (PVOID*)(&Process), 02125 NULL); 02126 if (NT_SUCCESS(Status)) 02127 { 02128 // 02129 // Do the copy 02130 // 02131 Status = MmCopyVirtualMemory(Process, 02132 BaseAddress, 02133 PsGetCurrentProcess(), 02134 Buffer, 02135 NumberOfBytesToRead, 02136 PreviousMode, 02137 &BytesRead); 02138 02139 // 02140 // Dereference the process 02141 // 02142 ObDereferenceObject(Process); 02143 } 02144 } 02145 02146 // 02147 // Check if the caller sent this parameter 02148 // 02149 if (NumberOfBytesRead) 02150 { 02151 // 02152 // Enter SEH to guard write 02153 // 02154 _SEH2_TRY 02155 { 02156 // 02157 // Return the number of bytes read 02158 // 02159 *NumberOfBytesRead = BytesRead; 02160 } 02161 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02162 { 02163 } 02164 _SEH2_END; 02165 } 02166 02167 // 02168 // Return status 02169 // 02170 return Status; 02171 } 02172 02173 NTSTATUS 02174 NTAPI 02175 NtWriteVirtualMemory(IN HANDLE ProcessHandle, 02176 IN PVOID BaseAddress, 02177 IN PVOID Buffer, 02178 IN SIZE_T NumberOfBytesToWrite, 02179 OUT PSIZE_T NumberOfBytesWritten OPTIONAL) 02180 { 02181 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 02182 PEPROCESS Process; 02183 NTSTATUS Status = STATUS_SUCCESS; 02184 SIZE_T BytesWritten = 0; 02185 PAGED_CODE(); 02186 02187 // 02188 // Check if we came from user mode 02189 // 02190 if (PreviousMode != KernelMode) 02191 { 02192 // 02193 // Validate the read addresses 02194 // 02195 if ((((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) < (ULONG_PTR)BaseAddress) || 02196 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) < (ULONG_PTR)Buffer) || 02197 (((ULONG_PTR)BaseAddress + NumberOfBytesToWrite) > MmUserProbeAddress) || 02198 (((ULONG_PTR)Buffer + NumberOfBytesToWrite) > MmUserProbeAddress)) 02199 { 02200 // 02201 // Don't allow to write into kernel space 02202 // 02203 return STATUS_ACCESS_VIOLATION; 02204 } 02205 02206 // 02207 // Enter SEH for probe 02208 // 02209 _SEH2_TRY 02210 { 02211 // 02212 // Probe the output value 02213 // 02214 if (NumberOfBytesWritten) ProbeForWriteSize_t(NumberOfBytesWritten); 02215 } 02216 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02217 { 02218 // 02219 // Get exception code 02220 // 02221 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02222 } 02223 _SEH2_END; 02224 } 02225 02226 // 02227 // Don't do zero-byte transfers 02228 // 02229 if (NumberOfBytesToWrite) 02230 { 02231 // 02232 // Reference the process 02233 // 02234 Status = ObReferenceObjectByHandle(ProcessHandle, 02235 PROCESS_VM_WRITE, 02236 PsProcessType, 02237 PreviousMode, 02238 (PVOID*)&Process, 02239 NULL); 02240 if (NT_SUCCESS(Status)) 02241 { 02242 // 02243 // Do the copy 02244 // 02245 Status = MmCopyVirtualMemory(PsGetCurrentProcess(), 02246 Buffer, 02247 Process, 02248 BaseAddress, 02249 NumberOfBytesToWrite, 02250 PreviousMode, 02251 &BytesWritten); 02252 02253 // 02254 // Dereference the process 02255 // 02256 ObDereferenceObject(Process); 02257 } 02258 } 02259 02260 // 02261 // Check if the caller sent this parameter 02262 // 02263 if (NumberOfBytesWritten) 02264 { 02265 // 02266 // Enter SEH to guard write 02267 // 02268 _SEH2_TRY 02269 { 02270 // 02271 // Return the number of bytes written 02272 // 02273 *NumberOfBytesWritten = BytesWritten; 02274 } 02275 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02276 { 02277 } 02278 _SEH2_END; 02279 } 02280 02281 // 02282 // Return status 02283 // 02284 return Status; 02285 } 02286 02287 NTSTATUS 02288 NTAPI 02289 NtProtectVirtualMemory(IN HANDLE ProcessHandle, 02290 IN OUT PVOID *UnsafeBaseAddress, 02291 IN OUT SIZE_T *UnsafeNumberOfBytesToProtect, 02292 IN ULONG NewAccessProtection, 02293 OUT PULONG UnsafeOldAccessProtection) 02294 { 02295 PEPROCESS Process; 02296 ULONG OldAccessProtection; 02297 ULONG Protection; 02298 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 02299 PVOID BaseAddress = NULL; 02300 SIZE_T NumberOfBytesToProtect = 0; 02301 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 02302 NTSTATUS Status; 02303 BOOLEAN Attached = FALSE; 02304 KAPC_STATE ApcState; 02305 PAGED_CODE(); 02306 02307 // 02308 // Check for valid protection flags 02309 // 02310 Protection = NewAccessProtection & ~(PAGE_GUARD|PAGE_NOCACHE); 02311 if (Protection != PAGE_NOACCESS && 02312 Protection != PAGE_READONLY && 02313 Protection != PAGE_READWRITE && 02314 Protection != PAGE_WRITECOPY && 02315 Protection != PAGE_EXECUTE && 02316 Protection != PAGE_EXECUTE_READ && 02317 Protection != PAGE_EXECUTE_READWRITE && 02318 Protection != PAGE_EXECUTE_WRITECOPY) 02319 { 02320 // 02321 // Fail 02322 // 02323 return STATUS_INVALID_PAGE_PROTECTION; 02324 } 02325 02326 // 02327 // Check if we came from user mode 02328 // 02329 if (PreviousMode != KernelMode) 02330 { 02331 // 02332 // Enter SEH for probing 02333 // 02334 _SEH2_TRY 02335 { 02336 // 02337 // Validate all outputs 02338 // 02339 ProbeForWritePointer(UnsafeBaseAddress); 02340 ProbeForWriteSize_t(UnsafeNumberOfBytesToProtect); 02341 ProbeForWriteUlong(UnsafeOldAccessProtection); 02342 02343 // 02344 // Capture them 02345 // 02346 BaseAddress = *UnsafeBaseAddress; 02347 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 02348 } 02349 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02350 { 02351 // 02352 // Get exception code 02353 // 02354 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02355 } 02356 _SEH2_END; 02357 } 02358 else 02359 { 02360 // 02361 // Capture directly 02362 // 02363 BaseAddress = *UnsafeBaseAddress; 02364 NumberOfBytesToProtect = *UnsafeNumberOfBytesToProtect; 02365 } 02366 02367 // 02368 // Catch illegal base address 02369 // 02370 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 02371 02372 // 02373 // Catch illegal region size 02374 // 02375 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < NumberOfBytesToProtect) 02376 { 02377 // 02378 // Fail 02379 // 02380 return STATUS_INVALID_PARAMETER_3; 02381 } 02382 02383 // 02384 // 0 is also illegal 02385 // 02386 if (!NumberOfBytesToProtect) return STATUS_INVALID_PARAMETER_3; 02387 02388 // 02389 // Get a reference to the process 02390 // 02391 Status = ObReferenceObjectByHandle(ProcessHandle, 02392 PROCESS_VM_OPERATION, 02393 PsProcessType, 02394 PreviousMode, 02395 (PVOID*)(&Process), 02396 NULL); 02397 if (!NT_SUCCESS(Status)) return Status; 02398 02399 // 02400 // Check if we should attach 02401 // 02402 if (CurrentProcess != Process) 02403 { 02404 // 02405 // Do it 02406 // 02407 KeStackAttachProcess(&Process->Pcb, &ApcState); 02408 Attached = TRUE; 02409 } 02410 02411 // 02412 // Do the actual work 02413 // 02414 Status = MiProtectVirtualMemory(Process, 02415 &BaseAddress, 02416 &NumberOfBytesToProtect, 02417 NewAccessProtection, 02418 &OldAccessProtection); 02419 02420 // 02421 // Detach if needed 02422 // 02423 if (Attached) KeUnstackDetachProcess(&ApcState); 02424 02425 // 02426 // Release reference 02427 // 02428 ObDereferenceObject(Process); 02429 02430 // 02431 // Enter SEH to return data 02432 // 02433 _SEH2_TRY 02434 { 02435 // 02436 // Return data to user 02437 // 02438 *UnsafeOldAccessProtection = OldAccessProtection; 02439 *UnsafeBaseAddress = BaseAddress; 02440 *UnsafeNumberOfBytesToProtect = NumberOfBytesToProtect; 02441 } 02442 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02443 { 02444 } 02445 _SEH2_END; 02446 02447 // 02448 // Return status 02449 // 02450 return Status; 02451 } 02452 02453 NTSTATUS 02454 NTAPI 02455 NtLockVirtualMemory(IN HANDLE ProcessHandle, 02456 IN OUT PVOID *BaseAddress, 02457 IN OUT PSIZE_T NumberOfBytesToLock, 02458 IN ULONG MapType) 02459 { 02460 PEPROCESS Process; 02461 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 02462 NTSTATUS Status; 02463 BOOLEAN Attached = FALSE; 02464 KAPC_STATE ApcState; 02465 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 02466 PVOID CapturedBaseAddress; 02467 SIZE_T CapturedBytesToLock; 02468 PAGED_CODE(); 02469 02470 // 02471 // Validate flags 02472 // 02473 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 02474 { 02475 // 02476 // Invalid set of flags 02477 // 02478 return STATUS_INVALID_PARAMETER; 02479 } 02480 02481 // 02482 // At least one flag must be specified 02483 // 02484 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 02485 { 02486 // 02487 // No flag given 02488 // 02489 return STATUS_INVALID_PARAMETER; 02490 } 02491 02492 // 02493 // Enter SEH for probing 02494 // 02495 _SEH2_TRY 02496 { 02497 // 02498 // Validate output data 02499 // 02500 ProbeForWritePointer(BaseAddress); 02501 ProbeForWriteSize_t(NumberOfBytesToLock); 02502 02503 // 02504 // Capture it 02505 // 02506 CapturedBaseAddress = *BaseAddress; 02507 CapturedBytesToLock = *NumberOfBytesToLock; 02508 } 02509 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02510 { 02511 // 02512 // Get exception code 02513 // 02514 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02515 } 02516 _SEH2_END; 02517 02518 // 02519 // Catch illegal base address 02520 // 02521 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 02522 02523 // 02524 // Catch illegal region size 02525 // 02526 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToLock) 02527 { 02528 // 02529 // Fail 02530 // 02531 return STATUS_INVALID_PARAMETER; 02532 } 02533 02534 // 02535 // 0 is also illegal 02536 // 02537 if (!CapturedBytesToLock) return STATUS_INVALID_PARAMETER; 02538 02539 // 02540 // Get a reference to the process 02541 // 02542 Status = ObReferenceObjectByHandle(ProcessHandle, 02543 PROCESS_VM_OPERATION, 02544 PsProcessType, 02545 PreviousMode, 02546 (PVOID*)(&Process), 02547 NULL); 02548 if (!NT_SUCCESS(Status)) return Status; 02549 02550 // 02551 // Check if this is is system-mapped 02552 // 02553 if (MapType & MAP_SYSTEM) 02554 { 02555 // 02556 // Check for required privilege 02557 // 02558 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 02559 { 02560 // 02561 // Fail: Don't have it 02562 // 02563 ObDereferenceObject(Process); 02564 return STATUS_PRIVILEGE_NOT_HELD; 02565 } 02566 } 02567 02568 // 02569 // Check if we should attach 02570 // 02571 if (CurrentProcess != Process) 02572 { 02573 // 02574 // Do it 02575 // 02576 KeStackAttachProcess(&Process->Pcb, &ApcState); 02577 Attached = TRUE; 02578 } 02579 02580 // 02581 // Oops :( 02582 // 02583 UNIMPLEMENTED; 02584 02585 // 02586 // Detach if needed 02587 // 02588 if (Attached) KeUnstackDetachProcess(&ApcState); 02589 02590 // 02591 // Release reference 02592 // 02593 ObDereferenceObject(Process); 02594 02595 // 02596 // Enter SEH to return data 02597 // 02598 _SEH2_TRY 02599 { 02600 // 02601 // Return data to user 02602 // 02603 *BaseAddress = CapturedBaseAddress; 02604 *NumberOfBytesToLock = 0; 02605 } 02606 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02607 { 02608 // 02609 // Get exception code 02610 // 02611 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02612 } 02613 _SEH2_END; 02614 02615 // 02616 // Return status 02617 // 02618 return STATUS_SUCCESS; 02619 } 02620 02621 NTSTATUS 02622 NTAPI 02623 NtUnlockVirtualMemory(IN HANDLE ProcessHandle, 02624 IN OUT PVOID *BaseAddress, 02625 IN OUT PSIZE_T NumberOfBytesToUnlock, 02626 IN ULONG MapType) 02627 { 02628 PEPROCESS Process; 02629 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 02630 NTSTATUS Status; 02631 BOOLEAN Attached = FALSE; 02632 KAPC_STATE ApcState; 02633 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 02634 PVOID CapturedBaseAddress; 02635 SIZE_T CapturedBytesToUnlock; 02636 PAGED_CODE(); 02637 02638 // 02639 // Validate flags 02640 // 02641 if ((MapType & ~(MAP_PROCESS | MAP_SYSTEM))) 02642 { 02643 // 02644 // Invalid set of flags 02645 // 02646 return STATUS_INVALID_PARAMETER; 02647 } 02648 02649 // 02650 // At least one flag must be specified 02651 // 02652 if (!(MapType & (MAP_PROCESS | MAP_SYSTEM))) 02653 { 02654 // 02655 // No flag given 02656 // 02657 return STATUS_INVALID_PARAMETER; 02658 } 02659 02660 // 02661 // Enter SEH for probing 02662 // 02663 _SEH2_TRY 02664 { 02665 // 02666 // Validate output data 02667 // 02668 ProbeForWritePointer(BaseAddress); 02669 ProbeForWriteSize_t(NumberOfBytesToUnlock); 02670 02671 // 02672 // Capture it 02673 // 02674 CapturedBaseAddress = *BaseAddress; 02675 CapturedBytesToUnlock = *NumberOfBytesToUnlock; 02676 } 02677 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02678 { 02679 // 02680 // Get exception code 02681 // 02682 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02683 } 02684 _SEH2_END; 02685 02686 // 02687 // Catch illegal base address 02688 // 02689 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 02690 02691 // 02692 // Catch illegal region size 02693 // 02694 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToUnlock) 02695 { 02696 // 02697 // Fail 02698 // 02699 return STATUS_INVALID_PARAMETER; 02700 } 02701 02702 // 02703 // 0 is also illegal 02704 // 02705 if (!CapturedBytesToUnlock) return STATUS_INVALID_PARAMETER; 02706 02707 // 02708 // Get a reference to the process 02709 // 02710 Status = ObReferenceObjectByHandle(ProcessHandle, 02711 PROCESS_VM_OPERATION, 02712 PsProcessType, 02713 PreviousMode, 02714 (PVOID*)(&Process), 02715 NULL); 02716 if (!NT_SUCCESS(Status)) return Status; 02717 02718 // 02719 // Check if this is is system-mapped 02720 // 02721 if (MapType & MAP_SYSTEM) 02722 { 02723 // 02724 // Check for required privilege 02725 // 02726 if (!SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode)) 02727 { 02728 // 02729 // Fail: Don't have it 02730 // 02731 ObDereferenceObject(Process); 02732 return STATUS_PRIVILEGE_NOT_HELD; 02733 } 02734 } 02735 02736 // 02737 // Check if we should attach 02738 // 02739 if (CurrentProcess != Process) 02740 { 02741 // 02742 // Do it 02743 // 02744 KeStackAttachProcess(&Process->Pcb, &ApcState); 02745 Attached = TRUE; 02746 } 02747 02748 // 02749 // Oops :( 02750 // 02751 UNIMPLEMENTED; 02752 02753 // 02754 // Detach if needed 02755 // 02756 if (Attached) KeUnstackDetachProcess(&ApcState); 02757 02758 // 02759 // Release reference 02760 // 02761 ObDereferenceObject(Process); 02762 02763 // 02764 // Enter SEH to return data 02765 // 02766 _SEH2_TRY 02767 { 02768 // 02769 // Return data to user 02770 // 02771 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress); 02772 *NumberOfBytesToUnlock = 0; 02773 } 02774 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02775 { 02776 // 02777 // Get exception code 02778 // 02779 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02780 } 02781 _SEH2_END; 02782 02783 // 02784 // Return status 02785 // 02786 return STATUS_SUCCESS; 02787 } 02788 02789 NTSTATUS 02790 NTAPI 02791 NtFlushVirtualMemory(IN HANDLE ProcessHandle, 02792 IN OUT PVOID *BaseAddress, 02793 IN OUT PSIZE_T NumberOfBytesToFlush, 02794 OUT PIO_STATUS_BLOCK IoStatusBlock) 02795 { 02796 PEPROCESS Process; 02797 NTSTATUS Status; 02798 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 02799 PVOID CapturedBaseAddress; 02800 SIZE_T CapturedBytesToFlush; 02801 IO_STATUS_BLOCK LocalStatusBlock; 02802 PAGED_CODE(); 02803 02804 // 02805 // Check if we came from user mode 02806 // 02807 if (PreviousMode != KernelMode) 02808 { 02809 // 02810 // Enter SEH for probing 02811 // 02812 _SEH2_TRY 02813 { 02814 // 02815 // Validate all outputs 02816 // 02817 ProbeForWritePointer(BaseAddress); 02818 ProbeForWriteSize_t(NumberOfBytesToFlush); 02819 ProbeForWriteIoStatusBlock(IoStatusBlock); 02820 02821 // 02822 // Capture them 02823 // 02824 CapturedBaseAddress = *BaseAddress; 02825 CapturedBytesToFlush = *NumberOfBytesToFlush; 02826 } 02827 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02828 { 02829 // 02830 // Get exception code 02831 // 02832 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02833 } 02834 _SEH2_END; 02835 } 02836 else 02837 { 02838 // 02839 // Capture directly 02840 // 02841 CapturedBaseAddress = *BaseAddress; 02842 CapturedBytesToFlush = *NumberOfBytesToFlush; 02843 } 02844 02845 // 02846 // Catch illegal base address 02847 // 02848 if (CapturedBaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 02849 02850 // 02851 // Catch illegal region size 02852 // 02853 if ((MmUserProbeAddress - (ULONG_PTR)CapturedBaseAddress) < CapturedBytesToFlush) 02854 { 02855 // 02856 // Fail 02857 // 02858 return STATUS_INVALID_PARAMETER; 02859 } 02860 02861 // 02862 // Get a reference to the process 02863 // 02864 Status = ObReferenceObjectByHandle(ProcessHandle, 02865 PROCESS_VM_OPERATION, 02866 PsProcessType, 02867 PreviousMode, 02868 (PVOID*)(&Process), 02869 NULL); 02870 if (!NT_SUCCESS(Status)) return Status; 02871 02872 // 02873 // Do it 02874 // 02875 Status = MmFlushVirtualMemory(Process, 02876 &CapturedBaseAddress, 02877 &CapturedBytesToFlush, 02878 &LocalStatusBlock); 02879 02880 // 02881 // Release reference 02882 // 02883 ObDereferenceObject(Process); 02884 02885 // 02886 // Enter SEH to return data 02887 // 02888 _SEH2_TRY 02889 { 02890 // 02891 // Return data to user 02892 // 02893 *BaseAddress = PAGE_ALIGN(CapturedBaseAddress); 02894 *NumberOfBytesToFlush = 0; 02895 *IoStatusBlock = LocalStatusBlock; 02896 } 02897 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02898 { 02899 } 02900 _SEH2_END; 02901 02902 // 02903 // Return status 02904 // 02905 return Status; 02906 } 02907 02908 /* 02909 * @unimplemented 02910 */ 02911 NTSTATUS 02912 NTAPI 02913 NtGetWriteWatch(IN HANDLE ProcessHandle, 02914 IN ULONG Flags, 02915 IN PVOID BaseAddress, 02916 IN SIZE_T RegionSize, 02917 IN PVOID *UserAddressArray, 02918 OUT PULONG_PTR EntriesInUserAddressArray, 02919 OUT PULONG Granularity) 02920 { 02921 PEPROCESS Process; 02922 NTSTATUS Status; 02923 PVOID EndAddress; 02924 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 02925 ULONG_PTR CapturedEntryCount; 02926 PAGED_CODE(); 02927 02928 // 02929 // Check if we came from user mode 02930 // 02931 if (PreviousMode != KernelMode) 02932 { 02933 // 02934 // Enter SEH for probing 02935 // 02936 _SEH2_TRY 02937 { 02938 // 02939 // Catch illegal base address 02940 // 02941 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 02942 02943 // 02944 // Catch illegal region size 02945 // 02946 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 02947 { 02948 // 02949 // Fail 02950 // 02951 return STATUS_INVALID_PARAMETER_3; 02952 } 02953 02954 // 02955 // Validate all data 02956 // 02957 ProbeForWriteSize_t(EntriesInUserAddressArray); 02958 ProbeForWriteUlong(Granularity); 02959 02960 // 02961 // Capture them 02962 // 02963 CapturedEntryCount = *EntriesInUserAddressArray; 02964 02965 // 02966 // Must have a count 02967 // 02968 if (CapturedEntryCount == 0) return STATUS_INVALID_PARAMETER_5; 02969 02970 // 02971 // Can't be larger than the maximum 02972 // 02973 if (CapturedEntryCount > (MAXULONG_PTR / sizeof(ULONG_PTR))) 02974 { 02975 // 02976 // Fail 02977 // 02978 return STATUS_INVALID_PARAMETER_5; 02979 } 02980 02981 // 02982 // Probe the actual array 02983 // 02984 ProbeForWrite(UserAddressArray, 02985 CapturedEntryCount * sizeof(PVOID), 02986 sizeof(PVOID)); 02987 } 02988 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 02989 { 02990 // 02991 // Get exception code 02992 // 02993 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 02994 } 02995 _SEH2_END; 02996 } 02997 else 02998 { 02999 // 03000 // Capture directly 03001 // 03002 CapturedEntryCount = *EntriesInUserAddressArray; 03003 ASSERT(CapturedEntryCount != 0); 03004 } 03005 03006 // 03007 // Check if this is a local request 03008 // 03009 if (ProcessHandle == NtCurrentProcess()) 03010 { 03011 // 03012 // No need to reference the process 03013 // 03014 Process = PsGetCurrentProcess(); 03015 } 03016 else 03017 { 03018 // 03019 // Reference the target 03020 // 03021 Status = ObReferenceObjectByHandle(ProcessHandle, 03022 PROCESS_VM_OPERATION, 03023 PsProcessType, 03024 PreviousMode, 03025 (PVOID *)&Process, 03026 NULL); 03027 if (!NT_SUCCESS(Status)) return Status; 03028 } 03029 03030 // 03031 // Compute the last address and validate it 03032 // 03033 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 03034 if (BaseAddress > EndAddress) 03035 { 03036 // 03037 // Fail 03038 // 03039 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 03040 return STATUS_INVALID_PARAMETER_4; 03041 } 03042 03043 // 03044 // Oops :( 03045 // 03046 UNIMPLEMENTED; 03047 03048 // 03049 // Dereference if needed 03050 // 03051 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 03052 03053 // 03054 // Enter SEH to return data 03055 // 03056 _SEH2_TRY 03057 { 03058 // 03059 // Return data to user 03060 // 03061 *EntriesInUserAddressArray = 0; 03062 *Granularity = PAGE_SIZE; 03063 } 03064 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 03065 { 03066 // 03067 // Get exception code 03068 // 03069 Status = _SEH2_GetExceptionCode(); 03070 } 03071 _SEH2_END; 03072 03073 // 03074 // Return success 03075 // 03076 return STATUS_SUCCESS; 03077 } 03078 03079 /* 03080 * @unimplemented 03081 */ 03082 NTSTATUS 03083 NTAPI 03084 NtResetWriteWatch(IN HANDLE ProcessHandle, 03085 IN PVOID BaseAddress, 03086 IN SIZE_T RegionSize) 03087 { 03088 PVOID EndAddress; 03089 PEPROCESS Process; 03090 NTSTATUS Status; 03091 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode(); 03092 ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL); 03093 03094 // 03095 // Catch illegal base address 03096 // 03097 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER_2; 03098 03099 // 03100 // Catch illegal region size 03101 // 03102 if ((MmUserProbeAddress - (ULONG_PTR)BaseAddress) < RegionSize) 03103 { 03104 // 03105 // Fail 03106 // 03107 return STATUS_INVALID_PARAMETER_3; 03108 } 03109 03110 // 03111 // Check if this is a local request 03112 // 03113 if (ProcessHandle == NtCurrentProcess()) 03114 { 03115 // 03116 // No need to reference the process 03117 // 03118 Process = PsGetCurrentProcess(); 03119 } 03120 else 03121 { 03122 // 03123 // Reference the target 03124 // 03125 Status = ObReferenceObjectByHandle(ProcessHandle, 03126 PROCESS_VM_OPERATION, 03127 PsProcessType, 03128 PreviousMode, 03129 (PVOID *)&Process, 03130 NULL); 03131 if (!NT_SUCCESS(Status)) return Status; 03132 } 03133 03134 // 03135 // Compute the last address and validate it 03136 // 03137 EndAddress = (PVOID)((ULONG_PTR)BaseAddress + RegionSize - 1); 03138 if (BaseAddress > EndAddress) 03139 { 03140 // 03141 // Fail 03142 // 03143 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 03144 return STATUS_INVALID_PARAMETER_3; 03145 } 03146 03147 // 03148 // Oops :( 03149 // 03150 UNIMPLEMENTED; 03151 03152 // 03153 // Dereference if needed 03154 // 03155 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 03156 03157 // 03158 // Return success 03159 // 03160 return STATUS_SUCCESS; 03161 } 03162 03163 NTSTATUS 03164 NTAPI 03165 NtQueryVirtualMemory(IN HANDLE ProcessHandle, 03166 IN PVOID BaseAddress, 03167 IN MEMORY_INFORMATION_CLASS MemoryInformationClass, 03168 OUT PVOID MemoryInformation, 03169 IN SIZE_T MemoryInformationLength, 03170 OUT PSIZE_T ReturnLength) 03171 { 03172 NTSTATUS Status = STATUS_SUCCESS; 03173 KPROCESSOR_MODE PreviousMode; 03174 03175 DPRINT("Querying class %d about address: %p\n", MemoryInformationClass, BaseAddress); 03176 03177 /* Bail out if the address is invalid */ 03178 if (BaseAddress > MM_HIGHEST_USER_ADDRESS) return STATUS_INVALID_PARAMETER; 03179 03180 /* Probe return buffer */ 03181 PreviousMode = ExGetPreviousMode(); 03182 if (PreviousMode != KernelMode) 03183 { 03184 _SEH2_TRY 03185 { 03186 ProbeForWrite(MemoryInformation, 03187 MemoryInformationLength, 03188 sizeof(ULONG_PTR)); 03189 03190 if (ReturnLength) ProbeForWriteSize_t(ReturnLength); 03191 } 03192 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 03193 { 03194 Status = _SEH2_GetExceptionCode(); 03195 } 03196 _SEH2_END; 03197 03198 if (!NT_SUCCESS(Status)) 03199 { 03200 return Status; 03201 } 03202 } 03203 03204 switch(MemoryInformationClass) 03205 { 03206 case MemoryBasicInformation: 03207 /* Validate the size information of the class */ 03208 if (MemoryInformationLength < sizeof(MEMORY_BASIC_INFORMATION)) 03209 { 03210 /* The size is invalid */ 03211 return STATUS_INFO_LENGTH_MISMATCH; 03212 } 03213 Status = MiQueryMemoryBasicInformation(ProcessHandle, 03214 BaseAddress, 03215 MemoryInformation, 03216 MemoryInformationLength, 03217 ReturnLength); 03218 break; 03219 03220 case MemorySectionName: 03221 /* Validate the size information of the class */ 03222 if (MemoryInformationLength < sizeof(MEMORY_SECTION_NAME)) 03223 { 03224 /* The size is invalid */ 03225 return STATUS_INFO_LENGTH_MISMATCH; 03226 } 03227 Status = MiQueryMemorySectionName(ProcessHandle, 03228 BaseAddress, 03229 MemoryInformation, 03230 MemoryInformationLength, 03231 ReturnLength); 03232 break; 03233 case MemoryWorkingSetList: 03234 case MemoryBasicVlmInformation: 03235 default: 03236 DPRINT1("Unhandled memory information class %d\n", MemoryInformationClass); 03237 break; 03238 } 03239 03240 return Status; 03241 } 03242 03243 /* 03244 * @implemented 03245 */ 03246 NTSTATUS 03247 NTAPI 03248 NtAllocateVirtualMemory(IN HANDLE ProcessHandle, 03249 IN OUT PVOID* UBaseAddress, 03250 IN ULONG_PTR ZeroBits, 03251 IN OUT PSIZE_T URegionSize, 03252 IN ULONG AllocationType, 03253 IN ULONG Protect) 03254 { 03255 PEPROCESS Process; 03256 PMEMORY_AREA MemoryArea; 03257 PFN_NUMBER PageCount; 03258 PMMVAD Vad, FoundVad; 03259 PUSHORT UsedPageTableEntries; 03260 NTSTATUS Status; 03261 PMMSUPPORT AddressSpace; 03262 PVOID PBaseAddress; 03263 ULONG_PTR PRegionSize, StartingAddress, EndingAddress; 03264 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 03265 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 03266 PETHREAD CurrentThread = PsGetCurrentThread(); 03267 KAPC_STATE ApcState; 03268 ULONG ProtectionMask, QuotaCharge = 0, QuotaFree = 0; 03269 BOOLEAN Attached = FALSE, ChangeProtection = FALSE; 03270 MMPTE TempPte; 03271 PMMPTE PointerPte, PointerPde, LastPte; 03272 PAGED_CODE(); 03273 03274 /* Check for valid Zero bits */ 03275 if (ZeroBits > 21) 03276 { 03277 DPRINT1("Too many zero bits\n"); 03278 return STATUS_INVALID_PARAMETER_3; 03279 } 03280 03281 /* Check for valid Allocation Types */ 03282 if ((AllocationType & ~(MEM_COMMIT | MEM_RESERVE | MEM_RESET | MEM_PHYSICAL | 03283 MEM_TOP_DOWN | MEM_WRITE_WATCH))) 03284 { 03285 DPRINT1("Invalid Allocation Type\n"); 03286 return STATUS_INVALID_PARAMETER_5; 03287 } 03288 03289 /* Check for at least one of these Allocation Types to be set */ 03290 if (!(AllocationType & (MEM_COMMIT | MEM_RESERVE | MEM_RESET))) 03291 { 03292 DPRINT1("No memory allocation base type\n"); 03293 return STATUS_INVALID_PARAMETER_5; 03294 } 03295 03296 /* MEM_RESET is an exclusive flag, make sure that is valid too */ 03297 if ((AllocationType & MEM_RESET) && (AllocationType != MEM_RESET)) 03298 { 03299 DPRINT1("Invalid use of MEM_RESET\n"); 03300 return STATUS_INVALID_PARAMETER_5; 03301 } 03302 03303 /* Check if large pages are being used */ 03304 if (AllocationType & MEM_LARGE_PAGES) 03305 { 03306 /* Large page allocations MUST be committed */ 03307 if (!(AllocationType & MEM_COMMIT)) 03308 { 03309 DPRINT1("Must supply MEM_COMMIT with MEM_LARGE_PAGES\n"); 03310 return STATUS_INVALID_PARAMETER_5; 03311 } 03312 03313 /* These flags are not allowed with large page allocations */ 03314 if (AllocationType & (MEM_PHYSICAL | MEM_RESET | MEM_WRITE_WATCH)) 03315 { 03316 DPRINT1("Using illegal flags with MEM_LARGE_PAGES\n"); 03317 return STATUS_INVALID_PARAMETER_5; 03318 } 03319 } 03320 03321 /* MEM_WRITE_WATCH can only be used if MEM_RESERVE is also used */ 03322 if ((AllocationType & MEM_WRITE_WATCH) && !(AllocationType & MEM_RESERVE)) 03323 { 03324 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n"); 03325 return STATUS_INVALID_PARAMETER_5; 03326 } 03327 03328 /* MEM_PHYSICAL can only be used if MEM_RESERVE is also used */ 03329 if ((AllocationType & MEM_PHYSICAL) && !(AllocationType & MEM_RESERVE)) 03330 { 03331 DPRINT1("MEM_WRITE_WATCH used without MEM_RESERVE\n"); 03332 return STATUS_INVALID_PARAMETER_5; 03333 } 03334 03335 /* Check for valid MEM_PHYSICAL usage */ 03336 if (AllocationType & MEM_PHYSICAL) 03337 { 03338 /* Only these flags are allowed with MEM_PHYSIAL */ 03339 if (AllocationType & ~(MEM_RESERVE | MEM_TOP_DOWN | MEM_PHYSICAL)) 03340 { 03341 DPRINT1("Using illegal flags with MEM_PHYSICAL\n"); 03342 return STATUS_INVALID_PARAMETER_5; 03343 } 03344 03345 /* Then make sure PAGE_READWRITE is used */ 03346 if (Protect != PAGE_READWRITE) 03347 { 03348 DPRINT1("MEM_PHYSICAL used without PAGE_READWRITE\n"); 03349 return STATUS_INVALID_PARAMETER_6; 03350 } 03351 } 03352 03353 // 03354 // Force PAGE_READWRITE for everything, for now 03355 // 03356 Protect = PAGE_READWRITE; 03357 03358 /* Calculate the protection mask and make sure it's valid */ 03359 ProtectionMask = MiMakeProtectionMask(Protect); 03360 if (ProtectionMask == MM_INVALID_PROTECTION) 03361 { 03362 DPRINT1("Invalid protection mask\n"); 03363 return STATUS_INVALID_PAGE_PROTECTION; 03364 } 03365 03366 /* Enter SEH */ 03367 _SEH2_TRY 03368 { 03369 /* Check for user-mode parameters */ 03370 if (PreviousMode != KernelMode) 03371 { 03372 /* Make sure they are writable */ 03373 ProbeForWritePointer(UBaseAddress); 03374 ProbeForWriteUlong(URegionSize); 03375 } 03376 03377 /* Capture their values */ 03378 PBaseAddress = *UBaseAddress; 03379 PRegionSize = *URegionSize; 03380 } 03381 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 03382 { 03383 /* Return the exception code */ 03384 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 03385 } 03386 _SEH2_END; 03387 03388 /* Make sure the allocation isn't past the VAD area */ 03389 if (PBaseAddress >= MM_HIGHEST_VAD_ADDRESS) 03390 { 03391 DPRINT1("Virtual allocation base above User Space\n"); 03392 return STATUS_INVALID_PARAMETER_2; 03393 } 03394 03395 /* Make sure the allocation wouldn't overflow past the VAD area */ 03396 if ((((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS + 1) - (ULONG_PTR)PBaseAddress) < PRegionSize) 03397 { 03398 DPRINT1("Region size would overflow into kernel-memory\n"); 03399 return STATUS_INVALID_PARAMETER_4; 03400 } 03401 03402 /* Make sure there's a size specified */ 03403 if (!PRegionSize) 03404 { 03405 DPRINT1("Region size is invalid (zero)\n"); 03406 return STATUS_INVALID_PARAMETER_4; 03407 } 03408 03409 // 03410 // If this is for the current process, just use PsGetCurrentProcess 03411 // 03412 if (ProcessHandle == NtCurrentProcess()) 03413 { 03414 Process = CurrentProcess; 03415 } 03416 else 03417 { 03418 // 03419 // Otherwise, reference the process with VM rights and attach to it if 03420 // this isn't the current process. We must attach because we'll be touching 03421 // PTEs and PDEs that belong to user-mode memory, and also touching the 03422 // Working Set which is stored in Hyperspace. 03423 // 03424 Status = ObReferenceObjectByHandle(ProcessHandle, 03425 PROCESS_VM_OPERATION, 03426 PsProcessType, 03427 PreviousMode, 03428 (PVOID*)&Process, 03429 NULL); 03430 if (!NT_SUCCESS(Status)) return Status; 03431 if (CurrentProcess != Process) 03432 { 03433 KeStackAttachProcess(&Process->Pcb, &ApcState); 03434 Attached = TRUE; 03435 } 03436 } 03437 03438 // 03439 // Check for large page allocations and make sure that the required privilege 03440 // is being held, before attempting to handle them. 03441 // 03442 if ((AllocationType & MEM_LARGE_PAGES) && 03443 !(SeSinglePrivilegeCheck(SeLockMemoryPrivilege, PreviousMode))) 03444 { 03445 /* Fail without it */ 03446 DPRINT1("Privilege not held for MEM_LARGE_PAGES\n"); 03447 Status = STATUS_PRIVILEGE_NOT_HELD; 03448 goto FailPathNoLock; 03449 } 03450 03451 // 03452 // Assert on the things we don't yet support 03453 // 03454 ASSERT(ZeroBits == 0); 03455 ASSERT((AllocationType & MEM_LARGE_PAGES) == 0); 03456 ASSERT((AllocationType & MEM_PHYSICAL) == 0); 03457 ASSERT((AllocationType & MEM_WRITE_WATCH) == 0); 03458 ASSERT((AllocationType & MEM_TOP_DOWN) == 0); 03459 ASSERT((AllocationType & MEM_RESET) == 0); 03460 ASSERT(Process->VmTopDown == 0); 03461 03462 // 03463 // Check if the caller is reserving memory, or committing memory and letting 03464 // us pick the base address 03465 // 03466 if (!(PBaseAddress) || (AllocationType & MEM_RESERVE)) 03467 { 03468 // 03469 // Do not allow COPY_ON_WRITE through this API 03470 // 03471 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY)) 03472 { 03473 DPRINT1("Copy on write not allowed through this path\n"); 03474 Status = STATUS_INVALID_PAGE_PROTECTION; 03475 goto FailPathNoLock; 03476 } 03477 03478 // 03479 // Does the caller have an address in mind, or is this a blind commit? 03480 // 03481 if (!PBaseAddress) 03482 { 03483 // 03484 // This is a blind commit, all we need is the region size 03485 // 03486 PRegionSize = ROUND_TO_PAGES(PRegionSize); 03487 PageCount = BYTES_TO_PAGES(PRegionSize); 03488 EndingAddress = 0; 03489 StartingAddress = 0; 03490 } 03491 else 03492 { 03493 // 03494 // This is a reservation, so compute the starting address on the 03495 // expected 64KB granularity, and see where the ending address will 03496 // fall based on the aligned address and the passed in region size 03497 // 03498 StartingAddress = ROUND_DOWN((ULONG_PTR)PBaseAddress, _64K); 03499 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 03500 PageCount = BYTES_TO_PAGES(EndingAddress - StartingAddress); 03501 } 03502 03503 // 03504 // Allocate and initialize the VAD 03505 // 03506 Vad = ExAllocatePoolWithTag(NonPagedPool, sizeof(MMVAD_LONG), 'SdaV'); 03507 ASSERT(Vad != NULL); 03508 Vad->u.LongFlags = 0; 03509 if (AllocationType & MEM_COMMIT) Vad->u.VadFlags.MemCommit = 1; 03510 Vad->u.VadFlags.Protection = ProtectionMask; 03511 Vad->u.VadFlags.PrivateMemory = 1; 03512 Vad->u.VadFlags.CommitCharge = AllocationType & MEM_COMMIT ? PageCount : 0; 03513 03514 // 03515 // Lock the address space and make sure the process isn't already dead 03516 // 03517 AddressSpace = MmGetCurrentAddressSpace(); 03518 MmLockAddressSpace(AddressSpace); 03519 if (Process->VmDeleted) 03520 { 03521 Status = STATUS_PROCESS_IS_TERMINATING; 03522 goto FailPath; 03523 } 03524 03525 // 03526 // Did we have a base address? If no, find a valid address that is 64KB 03527 // aligned in the VAD tree. Otherwise, make sure that the address range 03528 // which was passed in isn't already conflicting with an existing address 03529 // range. 03530 // 03531 if (!PBaseAddress) 03532 { 03533 Status = MiFindEmptyAddressRangeInTree(PRegionSize, 03534 _64K, 03535 &Process->VadRoot, 03536 (PMMADDRESS_NODE*)&Process->VadFreeHint, 03537 &StartingAddress); 03538 if (!NT_SUCCESS(Status)) goto FailPath; 03539 03540 // 03541 // Now we know where the allocation ends. Make sure it doesn't end up 03542 // somewhere in kernel mode. 03543 // 03544 EndingAddress = ((ULONG_PTR)StartingAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 03545 if ((PVOID)EndingAddress > MM_HIGHEST_VAD_ADDRESS) 03546 { 03547 Status = STATUS_NO_MEMORY; 03548 goto FailPath; 03549 } 03550 } 03551 else if (MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 03552 EndingAddress >> PAGE_SHIFT, 03553 &Process->VadRoot)) 03554 { 03555 // 03556 // The address specified is in conflict! 03557 // 03558 Status = STATUS_CONFLICTING_ADDRESSES; 03559 goto FailPath; 03560 } 03561 03562 // 03563 // Write out the VAD fields for this allocation 03564 // 03565 Vad->StartingVpn = (ULONG_PTR)StartingAddress >> PAGE_SHIFT; 03566 Vad->EndingVpn = (ULONG_PTR)EndingAddress >> PAGE_SHIFT; 03567 03568 // 03569 // FIXME: Should setup VAD bitmap 03570 // 03571 Status = STATUS_SUCCESS; 03572 03573 // 03574 // Lock the working set and insert the VAD into the process VAD tree 03575 // 03576 MiLockProcessWorkingSet(Process, CurrentThread); 03577 Vad->ControlArea = NULL; // For Memory-Area hack 03578 MiInsertVad(Vad, Process); 03579 MiUnlockProcessWorkingSet(Process, CurrentThread); 03580 03581 // 03582 // Update the virtual size of the process, and if this is now the highest 03583 // virtual size we have ever seen, update the peak virtual size to reflect 03584 // this. 03585 // 03586 Process->VirtualSize += PRegionSize; 03587 if (Process->VirtualSize > Process->PeakVirtualSize) 03588 { 03589 Process->PeakVirtualSize = Process->VirtualSize; 03590 } 03591 03592 // 03593 // Release address space and detach and dereference the target process if 03594 // it was different from the current process 03595 // 03596 MmUnlockAddressSpace(AddressSpace); 03597 if (Attached) KeUnstackDetachProcess(&ApcState); 03598 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 03599 03600 // 03601 // Use SEH to write back the base address and the region size. In the case 03602 // of an exception, we do not return back the exception code, as the memory 03603 // *has* been allocated. The caller would now have to call VirtualQuery 03604 // or do some other similar trick to actually find out where its memory 03605 // allocation ended up 03606 // 03607 _SEH2_TRY 03608 { 03609 *URegionSize = PRegionSize; 03610 *UBaseAddress = (PVOID)StartingAddress; 03611 } 03612 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 03613 { 03614 } 03615 _SEH2_END; 03616 return STATUS_SUCCESS; 03617 } 03618 03619 // 03620 // This is a MEM_COMMIT on top of an existing address which must have been 03621 // MEM_RESERVED already. Compute the start and ending base addresses based 03622 // on the user input, and then compute the actual region size once all the 03623 // alignments have been done. 03624 // 03625 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 03626 EndingAddress = (((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1)); 03627 PRegionSize = EndingAddress - StartingAddress + 1; 03628 03629 // 03630 // Lock the address space and make sure the process isn't already dead 03631 // 03632 AddressSpace = MmGetCurrentAddressSpace(); 03633 MmLockAddressSpace(AddressSpace); 03634 if (Process->VmDeleted) 03635 { 03636 DPRINT1("Process is dying\n"); 03637 Status = STATUS_PROCESS_IS_TERMINATING; 03638 goto FailPath; 03639 } 03640 03641 // 03642 // Get the VAD for this address range, and make sure it exists 03643 // 03644 FoundVad = (PMMVAD)MiCheckForConflictingNode(StartingAddress >> PAGE_SHIFT, 03645 EndingAddress >> PAGE_SHIFT, 03646 &Process->VadRoot); 03647 if (!FoundVad) 03648 { 03649 DPRINT1("Could not find a VAD for this allocation\n"); 03650 Status = STATUS_CONFLICTING_ADDRESSES; 03651 goto FailPath; 03652 } 03653 03654 // 03655 // These kinds of VADs are illegal for this Windows function when trying to 03656 // commit an existing range 03657 // 03658 if ((FoundVad->u.VadFlags.VadType == VadAwe) || 03659 (FoundVad->u.VadFlags.VadType == VadDevicePhysicalMemory) || 03660 (FoundVad->u.VadFlags.VadType == VadLargePages)) 03661 { 03662 DPRINT1("Illegal VAD for attempting a MEM_COMMIT\n"); 03663 Status = STATUS_CONFLICTING_ADDRESSES; 03664 goto FailPath; 03665 } 03666 03667 // 03668 // Make sure that this address range actually fits within the VAD for it 03669 // 03670 if (((StartingAddress >> PAGE_SHIFT) < FoundVad->StartingVpn) && 03671 ((EndingAddress >> PAGE_SHIFT) > FoundVad->EndingVpn)) 03672 { 03673 DPRINT1("Address range does not fit into the VAD\n"); 03674 Status = STATUS_CONFLICTING_ADDRESSES; 03675 goto FailPath; 03676 } 03677 03678 // 03679 // If this is an existing section view, we call the old RosMm routine which 03680 // has the relevant code required to handle the section scenario. In the future 03681 // we will limit this even more so that there's almost nothing that the code 03682 // needs to do, and it will become part of section.c in RosMm 03683 // 03684 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)PAGE_ROUND_DOWN(PBaseAddress)); 03685 if (MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3) 03686 { 03687 return MiRosAllocateVirtualMemory(ProcessHandle, 03688 Process, 03689 MemoryArea, 03690 AddressSpace, 03691 UBaseAddress, 03692 Attached, 03693 URegionSize, 03694 AllocationType, 03695 Protect); 03696 } 03697 03698 // Is this a previously reserved section being committed? If so, enter the 03699 // special section path 03700 // 03701 if (FoundVad->u.VadFlags.PrivateMemory == FALSE) 03702 { 03703 // 03704 // You cannot commit large page sections through this API 03705 // 03706 if (FoundVad->u.VadFlags.VadType == VadLargePageSection) 03707 { 03708 DPRINT1("Large page sections cannot be VirtualAlloc'd\n"); 03709 Status = STATUS_INVALID_PAGE_PROTECTION; 03710 goto FailPath; 03711 } 03712 03713 // 03714 // You can only use caching flags on a rotate VAD 03715 // 03716 if ((Protect & (PAGE_NOCACHE | PAGE_WRITECOMBINE)) && 03717 (FoundVad->u.VadFlags.VadType != VadRotatePhysical)) 03718 { 03719 DPRINT1("Cannot use caching flags with anything but rotate VADs\n"); 03720 Status = STATUS_INVALID_PAGE_PROTECTION; 03721 goto FailPath; 03722 } 03723 03724 // 03725 // We should make sure that the section's permissions aren't being messed with 03726 // 03727 if (FoundVad->u.VadFlags.NoChange) 03728 { 03729 DPRINT1("SEC_NO_CHANGE section being touched. Assuming this is ok\n"); 03730 } 03731 03732 // 03733 // ARM3 does not support file-backed sections, only shared memory 03734 // 03735 ASSERT(FoundVad->ControlArea->FilePointer == NULL); 03736 03737 // 03738 // Rotate VADs cannot be guard pages or inaccessible, nor copy on write 03739 // 03740 if ((FoundVad->u.VadFlags.VadType == VadRotatePhysical) && 03741 (Protect & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY | PAGE_NOACCESS | PAGE_GUARD))) 03742 { 03743 DPRINT1("Invalid page protection for rotate VAD\n"); 03744 Status = STATUS_INVALID_PAGE_PROTECTION; 03745 goto FailPath; 03746 } 03747 03748 // 03749 // Compute PTE addresses and the quota charge, then grab the commit lock 03750 // 03751 PointerPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, StartingAddress >> PAGE_SHIFT); 03752 LastPte = MI_GET_PROTOTYPE_PTE_FOR_VPN(FoundVad, EndingAddress >> PAGE_SHIFT); 03753 QuotaCharge = (ULONG)(LastPte - PointerPte + 1); 03754 KeAcquireGuardedMutexUnsafe(&MmSectionCommitMutex); 03755 03756 // 03757 // Get the segment template PTE and start looping each page 03758 // 03759 TempPte = FoundVad->ControlArea->Segment->SegmentPteTemplate; 03760 ASSERT(TempPte.u.Long != 0); 03761 while (PointerPte <= LastPte) 03762 { 03763 // 03764 // For each non-already-committed page, write the invalid template PTE 03765 // 03766 if (PointerPte->u.Long == 0) 03767 { 03768 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 03769 } 03770 else 03771 { 03772 QuotaFree++; 03773 } 03774 PointerPte++; 03775 } 03776 03777 // 03778 // Now do the commit accounting and release the lock 03779 // 03780 ASSERT(QuotaCharge >= QuotaFree); 03781 QuotaCharge -= QuotaFree; 03782 FoundVad->ControlArea->Segment->NumberOfCommittedPages += QuotaCharge; 03783 KeReleaseGuardedMutexUnsafe(&MmSectionCommitMutex); 03784 03785 // 03786 // We are done with committing the section pages 03787 // 03788 Status = STATUS_SUCCESS; 03789 goto FailPath; 03790 } 03791 03792 // 03793 // This is a specific ReactOS check because we only use normal VADs 03794 // 03795 ASSERT(FoundVad->u.VadFlags.VadType == VadNone); 03796 03797 // 03798 // While this is an actual Windows check 03799 // 03800 ASSERT(FoundVad->u.VadFlags.VadType != VadRotatePhysical); 03801 03802 // 03803 // Throw out attempts to use copy-on-write through this API path 03804 // 03805 if ((Protect & PAGE_WRITECOPY) || (Protect & PAGE_EXECUTE_WRITECOPY)) 03806 { 03807 DPRINT1("Write copy attempted when not allowed\n"); 03808 Status = STATUS_INVALID_PAGE_PROTECTION; 03809 goto FailPath; 03810 } 03811 03812 // 03813 // Initialize a demand-zero PTE 03814 // 03815 TempPte.u.Long = 0; 03816 TempPte.u.Soft.Protection = ProtectionMask; 03817 03818 // 03819 // Get the PTE, PDE and the last PTE for this address range 03820 // 03821 PointerPde = MiAddressToPde(StartingAddress); 03822 PointerPte = MiAddressToPte(StartingAddress); 03823 LastPte = MiAddressToPte(EndingAddress); 03824 03825 // 03826 // Update the commit charge in the VAD as well as in the process, and check 03827 // if this commit charge was now higher than the last recorded peak, in which 03828 // case we also update the peak 03829 // 03830 FoundVad->u.VadFlags.CommitCharge += (1 + LastPte - PointerPte); 03831 Process->CommitCharge += (1 + LastPte - PointerPte); 03832 if (Process->CommitCharge > Process->CommitChargePeak) 03833 { 03834 Process->CommitChargePeak = Process->CommitCharge; 03835 } 03836 03837 // 03838 // Lock the working set while we play with user pages and page tables 03839 // 03840 //MiLockWorkingSet(CurrentThread, AddressSpace); 03841 03842 // 03843 // Make the current page table valid, and then loop each page within it 03844 // 03845 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 03846 while (PointerPte <= LastPte) 03847 { 03848 // 03849 // Have we crossed into a new page table? 03850 // 03851 if (!(((ULONG_PTR)PointerPte) & (SYSTEM_PD_SIZE - 1))) 03852 { 03853 // 03854 // Get the PDE and now make it valid too 03855 // 03856 PointerPde = MiAddressToPte(PointerPte); 03857 MiMakePdeExistAndMakeValid(PointerPde, Process, MM_NOIRQL); 03858 } 03859 03860 // 03861 // Is this a zero PTE as expected? 03862 // 03863 if (PointerPte->u.Long == 0) 03864 { 03865 // 03866 // First increment the count of pages in the page table for this 03867 // process 03868 // 03869 UsedPageTableEntries = &MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(MiPteToAddress(PointerPte))]; 03870 (*UsedPageTableEntries)++; 03871 ASSERT((*UsedPageTableEntries) <= PTE_COUNT); 03872 03873 // 03874 // And now write the invalid demand-zero PTE as requested 03875 // 03876 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 03877 } 03878 else if (PointerPte->u.Long == MmDecommittedPte.u.Long) 03879 { 03880 // 03881 // If the PTE was already decommitted, there is nothing else to do 03882 // but to write the new demand-zero PTE 03883 // 03884 MI_WRITE_INVALID_PTE(PointerPte, TempPte); 03885 } 03886 else if (!(ChangeProtection) && (Protect != MiGetPageProtection(PointerPte))) 03887 { 03888 // 03889 // We don't handle these scenarios yet 03890 // 03891 if (PointerPte->u.Soft.Valid == 0) 03892 { 03893 ASSERT(PointerPte->u.Soft.Prototype == 0); 03894 ASSERT(PointerPte->u.Soft.PageFileHigh == 0); 03895 } 03896 03897 // 03898 // There's a change in protection, remember this for later, but do 03899 // not yet handle it. 03900 // 03901 DPRINT1("Protection change to: 0x%lx not implemented\n", Protect); 03902 ChangeProtection = TRUE; 03903 } 03904 03905 // 03906 // Move to the next PTE 03907 // 03908 PointerPte++; 03909 } 03910 03911 // 03912 // This path is not yet handled 03913 // 03914 ASSERT(ChangeProtection == FALSE); 03915 03916 // 03917 // Release the working set lock, unlock the address space, and detach from 03918 // the target process if it was not the current process. Also dereference the 03919 // target process if this wasn't the case. 03920 // 03921 //MiUnlockProcessWorkingSet(Process, CurrentThread); 03922 Status = STATUS_SUCCESS; 03923 FailPath: 03924 MmUnlockAddressSpace(AddressSpace); 03925 FailPathNoLock: 03926 if (Attached) KeUnstackDetachProcess(&ApcState); 03927 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 03928 03929 // 03930 // Use SEH to write back the base address and the region size. In the case 03931 // of an exception, we strangely do return back the exception code, even 03932 // though the memory *has* been allocated. This mimics Windows behavior and 03933 // there is not much we can do about it. 03934 // 03935 _SEH2_TRY 03936 { 03937 *URegionSize = PRegionSize; 03938 *UBaseAddress = (PVOID)StartingAddress; 03939 } 03940 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 03941 { 03942 Status = _SEH2_GetExceptionCode(); 03943 } 03944 _SEH2_END; 03945 return Status; 03946 } 03947 03948 /* 03949 * @implemented 03950 */ 03951 NTSTATUS 03952 NTAPI 03953 NtFreeVirtualMemory(IN HANDLE ProcessHandle, 03954 IN PVOID* UBaseAddress, 03955 IN PSIZE_T URegionSize, 03956 IN ULONG FreeType) 03957 { 03958 PMEMORY_AREA MemoryArea; 03959 SIZE_T PRegionSize; 03960 PVOID PBaseAddress; 03961 ULONG_PTR CommitReduction = 0; 03962 ULONG_PTR StartingAddress, EndingAddress; 03963 PMMVAD Vad; 03964 NTSTATUS Status; 03965 PEPROCESS Process; 03966 PMMSUPPORT AddressSpace; 03967 PETHREAD CurrentThread = PsGetCurrentThread(); 03968 PEPROCESS CurrentProcess = PsGetCurrentProcess(); 03969 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 03970 KAPC_STATE ApcState; 03971 BOOLEAN Attached = FALSE; 03972 PAGED_CODE(); 03973 03974 // 03975 // Only two flags are supported 03976 // 03977 if (!(FreeType & (MEM_RELEASE | MEM_DECOMMIT))) 03978 { 03979 DPRINT1("Invalid FreeType\n"); 03980 return STATUS_INVALID_PARAMETER_4; 03981 } 03982 03983 // 03984 // Check if no flag was used, or if both flags were used 03985 // 03986 if (!((FreeType & (MEM_DECOMMIT | MEM_RELEASE))) || 03987 ((FreeType & (MEM_DECOMMIT | MEM_RELEASE)) == (MEM_DECOMMIT | MEM_RELEASE))) 03988 { 03989 DPRINT1("Invalid FreeType combination\n"); 03990 return STATUS_INVALID_PARAMETER_4; 03991 } 03992 03993 // 03994 // Enter SEH for probe and capture. On failure, return back to the caller 03995 // with an exception violation. 03996 // 03997 _SEH2_TRY 03998 { 03999 // 04000 // Check for user-mode parameters and make sure that they are writeable 04001 // 04002 if (PreviousMode != KernelMode) 04003 { 04004 ProbeForWritePointer(UBaseAddress); 04005 ProbeForWriteUlong(URegionSize); 04006 } 04007 04008 // 04009 // Capture the current values 04010 // 04011 PBaseAddress = *UBaseAddress; 04012 PRegionSize = *URegionSize; 04013 } 04014 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 04015 { 04016 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 04017 } 04018 _SEH2_END; 04019 04020 // 04021 // Make sure the allocation isn't past the user area 04022 // 04023 if (PBaseAddress >= MM_HIGHEST_USER_ADDRESS) 04024 { 04025 DPRINT1("Virtual free base above User Space\n"); 04026 return STATUS_INVALID_PARAMETER_2; 04027 } 04028 04029 // 04030 // Make sure the allocation wouldn't overflow past the user area 04031 // 04032 if (((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (ULONG_PTR)PBaseAddress) < PRegionSize) 04033 { 04034 DPRINT1("Region size would overflow into kernel-memory\n"); 04035 return STATUS_INVALID_PARAMETER_3; 04036 } 04037 04038 // 04039 // If this is for the current process, just use PsGetCurrentProcess 04040 // 04041 if (ProcessHandle == NtCurrentProcess()) 04042 { 04043 Process = CurrentProcess; 04044 } 04045 else 04046 { 04047 // 04048 // Otherwise, reference the process with VM rights and attach to it if 04049 // this isn't the current process. We must attach because we'll be touching 04050 // PTEs and PDEs that belong to user-mode memory, and also touching the 04051 // Working Set which is stored in Hyperspace. 04052 // 04053 Status = ObReferenceObjectByHandle(ProcessHandle, 04054 PROCESS_VM_OPERATION, 04055 PsProcessType, 04056 PreviousMode, 04057 (PVOID*)&Process, 04058 NULL); 04059 if (!NT_SUCCESS(Status)) return Status; 04060 if (CurrentProcess != Process) 04061 { 04062 KeStackAttachProcess(&Process->Pcb, &ApcState); 04063 Attached = TRUE; 04064 } 04065 } 04066 04067 // 04068 // Lock the address space 04069 // 04070 AddressSpace = MmGetCurrentAddressSpace(); 04071 MmLockAddressSpace(AddressSpace); 04072 04073 // 04074 // If the address space is being deleted, fail the de-allocation since it's 04075 // too late to do anything about it 04076 // 04077 if (Process->VmDeleted) 04078 { 04079 DPRINT1("Process is dead\n"); 04080 Status = STATUS_PROCESS_IS_TERMINATING; 04081 goto FailPath; 04082 } 04083 04084 // 04085 // Compute start and end addresses, and locate the VAD 04086 // 04087 StartingAddress = (ULONG_PTR)PAGE_ALIGN(PBaseAddress); 04088 EndingAddress = ((ULONG_PTR)PBaseAddress + PRegionSize - 1) | (PAGE_SIZE - 1); 04089 Vad = MiLocateAddress((PVOID)StartingAddress); 04090 if (!Vad) 04091 { 04092 DPRINT1("Unable to find VAD for address 0x%p\n", StartingAddress); 04093 Status = STATUS_MEMORY_NOT_ALLOCATED; 04094 goto FailPath; 04095 } 04096 04097 // 04098 // If the range exceeds the VAD's ending VPN, fail this request 04099 // 04100 if (Vad->EndingVpn < (EndingAddress >> PAGE_SHIFT)) 04101 { 04102 DPRINT1("Address 0x%p is beyond the VAD\n", EndingAddress); 04103 Status = STATUS_UNABLE_TO_FREE_VM; 04104 goto FailPath; 04105 } 04106 04107 // 04108 // These ASSERTs are here because ReactOS ARM3 does not currently implement 04109 // any other kinds of VADs. 04110 // 04111 ASSERT(Vad->u.VadFlags.PrivateMemory == 1); 04112 ASSERT(Vad->u.VadFlags.NoChange == 0); 04113 ASSERT(Vad->u.VadFlags.VadType == VadNone); 04114 04115 // 04116 // Finally, make sure there is a ReactOS Mm MEMORY_AREA for this allocation 04117 // and that is is an ARM3 memory area, and not a section view, as we currently 04118 // don't support freeing those though this interface. 04119 // 04120 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)StartingAddress); 04121 ASSERT(MemoryArea); 04122 ASSERT(MemoryArea->Type == MEMORY_AREA_OWNED_BY_ARM3); 04123 04124 // 04125 // Now we can try the operation. First check if this is a RELEASE or a DECOMMIT 04126 // 04127 if (FreeType & MEM_RELEASE) 04128 { 04129 // 04130 // Is the caller trying to remove the whole VAD, or remove only a portion 04131 // of it? If no region size is specified, then the assumption is that the 04132 // whole VAD is to be destroyed 04133 // 04134 if (!PRegionSize) 04135 { 04136 // 04137 // The caller must specify the base address identically to the range 04138 // that is stored in the VAD. 04139 // 04140 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 04141 { 04142 DPRINT1("Address 0x%p does not match the VAD\n", PBaseAddress); 04143 Status = STATUS_FREE_VM_NOT_AT_BASE; 04144 goto FailPath; 04145 } 04146 04147 // 04148 // Now compute the actual start/end addresses based on the VAD 04149 // 04150 StartingAddress = Vad->StartingVpn << PAGE_SHIFT; 04151 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 04152 04153 // 04154 // Finally lock the working set and remove the VAD from the VAD tree 04155 // 04156 MiLockWorkingSet(CurrentThread, AddressSpace); 04157 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 04158 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 04159 } 04160 else 04161 { 04162 // 04163 // This means the caller wants to release a specific region within 04164 // the range. We have to find out which range this is -- the following 04165 // possibilities exist plus their union (CASE D): 04166 // 04167 // STARTING ADDRESS ENDING ADDRESS 04168 // [<========][========================================][=========>] 04169 // CASE A CASE B CASE C 04170 // 04171 // 04172 // First, check for case A or D 04173 // 04174 if ((StartingAddress >> PAGE_SHIFT) == Vad->StartingVpn) 04175 { 04176 // 04177 // Check for case D 04178 // 04179 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 04180 { 04181 // 04182 // This is the easiest one to handle -- it is identical to 04183 // the code path above when the caller sets a zero region size 04184 // and the whole VAD is destroyed 04185 // 04186 MiLockWorkingSet(CurrentThread, AddressSpace); 04187 ASSERT(Process->VadRoot.NumberGenericTableElements >= 1); 04188 MiRemoveNode((PMMADDRESS_NODE)Vad, &Process->VadRoot); 04189 } 04190 else 04191 { 04192 // 04193 // This case is pretty easy too -- we compute a bunch of 04194 // pages to decommit, and then push the VAD's starting address 04195 // a bit further down, then decrement the commit charge 04196 // 04197 // NOT YET IMPLEMENTED IN ARM3. 04198 // 04199 DPRINT1("Case A not handled\n"); 04200 Status = STATUS_FREE_VM_NOT_AT_BASE; 04201 goto FailPath; 04202 04203 // 04204 // After analyzing the VAD, set it to NULL so that we don't 04205 // free it in the exit path 04206 // 04207 Vad = NULL; 04208 } 04209 } 04210 else 04211 { 04212 // 04213 // This is case B or case C. First check for case C 04214 // 04215 if ((EndingAddress >> PAGE_SHIFT) == Vad->EndingVpn) 04216 { 04217 // 04218 // This is pretty easy and similar to case A. We compute the 04219 // amount of pages to decommit, update the VAD's commit charge 04220 // and then change the ending address of the VAD to be a bit 04221 // smaller. 04222 // 04223 MiLockWorkingSet(CurrentThread, AddressSpace); 04224 CommitReduction = MiCalculatePageCommitment(StartingAddress, 04225 EndingAddress, 04226 Vad, 04227 Process); 04228 Vad->u.VadFlags.CommitCharge -= CommitReduction; 04229 Vad->EndingVpn = ((ULONG_PTR)StartingAddress - 1) >> PAGE_SHIFT; 04230 } 04231 else 04232 { 04233 // 04234 // This is case B and the hardest one. Because we are removing 04235 // a chunk of memory from the very middle of the VAD, we must 04236 // actually split the VAD into two new VADs and compute the 04237 // commit charges for each of them, and reinsert new charges. 04238 // 04239 // NOT YET IMPLEMENTED IN ARM3. 04240 // 04241 DPRINT1("Case B not handled\n"); 04242 Status = STATUS_FREE_VM_NOT_AT_BASE; 04243 goto FailPath; 04244 } 04245 04246 // 04247 // After analyzing the VAD, set it to NULL so that we don't 04248 // free it in the exit path 04249 // 04250 Vad = NULL; 04251 } 04252 } 04253 04254 // 04255 // Now we have a range of pages to dereference, so call the right API 04256 // to do that and then release the working set, since we're done messing 04257 // around with process pages. 04258 // 04259 MiDeleteVirtualAddresses(StartingAddress, EndingAddress, NULL); 04260 MiUnlockWorkingSet(CurrentThread, AddressSpace); 04261 Status = STATUS_SUCCESS; 04262 04263 FinalPath: 04264 // 04265 // Update the process counters 04266 // 04267 PRegionSize = EndingAddress - StartingAddress + 1; 04268 Process->CommitCharge -= CommitReduction; 04269 if (FreeType & MEM_RELEASE) Process->VirtualSize -= PRegionSize; 04270 04271 // 04272 // Unlock the address space and free the VAD in failure cases. Next, 04273 // detach from the target process so we can write the region size and the 04274 // base address to the correct source process, and dereference the target 04275 // process. 04276 // 04277 MmUnlockAddressSpace(AddressSpace); 04278 if (Vad) ExFreePool(Vad); 04279 if (Attached) KeUnstackDetachProcess(&ApcState); 04280 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 04281 04282 // 04283 // Use SEH to safely return the region size and the base address of the 04284 // deallocation. If we get an access violation, don't return a failure code 04285 // as the deallocation *has* happened. The caller will just have to figure 04286 // out another way to find out where it is (such as VirtualQuery). 04287 // 04288 _SEH2_TRY 04289 { 04290 *URegionSize = PRegionSize; 04291 *UBaseAddress = (PVOID)StartingAddress; 04292 } 04293 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 04294 { 04295 } 04296 _SEH2_END; 04297 return Status; 04298 } 04299 04300 // 04301 // This is the decommit path. You cannot decommit from the following VADs in 04302 // Windows, so fail the vall 04303 // 04304 if ((Vad->u.VadFlags.VadType == VadAwe) || 04305 (Vad->u.VadFlags.VadType == VadLargePages) || 04306 (Vad->u.VadFlags.VadType == VadRotatePhysical)) 04307 { 04308 DPRINT1("Trying to decommit from invalid VAD\n"); 04309 Status = STATUS_MEMORY_NOT_ALLOCATED; 04310 goto FailPath; 04311 } 04312 04313 // 04314 // If the caller did not specify a region size, first make sure that this 04315 // region is actually committed. If it is, then compute the ending address 04316 // based on the VAD. 04317 // 04318 if (!PRegionSize) 04319 { 04320 if (((ULONG_PTR)PBaseAddress >> PAGE_SHIFT) != Vad->StartingVpn) 04321 { 04322 DPRINT1("Decomitting non-committed memory\n"); 04323 Status = STATUS_FREE_VM_NOT_AT_BASE; 04324 goto FailPath; 04325 } 04326 EndingAddress = (Vad->EndingVpn << PAGE_SHIFT) | (PAGE_SIZE - 1); 04327 } 04328 04329 // 04330 // Decommit the PTEs for the range plus the actual backing pages for the 04331 // range, then reduce that amount from the commit charge in the VAD 04332 // 04333 CommitReduction = MiAddressToPte(EndingAddress) - 04334 MiAddressToPte(StartingAddress) + 04335 1 - 04336 MiDecommitPages((PVOID)StartingAddress, 04337 MiAddressToPte(EndingAddress), 04338 Process, 04339 Vad); 04340 ASSERT(CommitReduction >= 0); 04341 Vad->u.VadFlags.CommitCharge -= CommitReduction; 04342 ASSERT(Vad->u.VadFlags.CommitCharge >= 0); 04343 04344 // 04345 // We are done, go to the exit path without freeing the VAD as it remains 04346 // valid since we have not released the allocation. 04347 // 04348 Vad = NULL; 04349 Status = STATUS_SUCCESS; 04350 goto FinalPath; 04351 04352 // 04353 // In the failure path, we detach and derefernece the target process, and 04354 // return whatever failure code was sent. 04355 // 04356 FailPath: 04357 MmUnlockAddressSpace(AddressSpace); 04358 if (Attached) KeUnstackDetachProcess(&ApcState); 04359 if (ProcessHandle != NtCurrentProcess()) ObDereferenceObject(Process); 04360 return Status; 04361 } 04362 04363 /* EOF */ Generated on Fri May 25 2012 04:36:03 for ReactOS by
1.7.6.1
|