ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

virtual.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.