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

pagfault.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/pagfault.c
00005  * PURPOSE:         ARM Memory Manager Page Fault Handling
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 /* GLOBALS ********************************************************************/
00019 
00020 #if MI_TRACE_PFNS
00021 BOOLEAN UserPdeFault = FALSE;
00022 #endif
00023 
00024 /* PRIVATE FUNCTIONS **********************************************************/
00025 
00026 PMMPTE
00027 NTAPI
00028 MiCheckVirtualAddress(IN PVOID VirtualAddress,
00029                       OUT PULONG ProtectCode,
00030                       OUT PMMVAD *ProtoVad)
00031 {
00032     PMMVAD Vad;
00033     PMMPTE PointerPte;
00034 
00035     /* No prototype/section support for now */
00036     *ProtoVad = NULL;
00037 
00038     /* Check if this is a page table address */
00039     if (MI_IS_PAGE_TABLE_ADDRESS(VirtualAddress))
00040     {
00041         /* This should never happen, as these addresses are handled by the double-maping */
00042         if (((PMMPTE)VirtualAddress >= MiAddressToPte(MmPagedPoolStart)) &&
00043             ((PMMPTE)VirtualAddress <= MmPagedPoolInfo.LastPteForPagedPool))
00044         {
00045             /* Fail such access */
00046             *ProtectCode = MM_NOACCESS;
00047             return NULL;
00048         }
00049 
00050         /* Return full access rights */
00051         *ProtectCode = MM_READWRITE;
00052         return NULL;
00053     }
00054 
00055     /* Should not be a session address */
00056     ASSERT(MI_IS_SESSION_ADDRESS(VirtualAddress) == FALSE);
00057 
00058     /* Special case for shared data */
00059     if (PAGE_ALIGN(VirtualAddress) == (PVOID)MM_SHARED_USER_DATA_VA)
00060     {
00061         /* It's a read-only page */
00062         *ProtectCode = MM_READONLY;
00063         return MmSharedUserDataPte;
00064     }
00065 
00066     /* Find the VAD, it might not exist if the address is bogus */
00067     Vad = MiLocateAddress(VirtualAddress);
00068     if (!Vad)
00069     {
00070         /* Bogus virtual address */
00071         *ProtectCode = MM_NOACCESS;
00072         return NULL;
00073     }
00074 
00075     /* This must be a VM VAD */
00076     ASSERT(Vad->u.VadFlags.VadType == VadNone);
00077 
00078     /* Check if it's a section, or just an allocation */
00079     if (Vad->u.VadFlags.PrivateMemory)
00080     {
00081         /* This must be a TEB/PEB VAD */
00082         if (Vad->u.VadFlags.MemCommit)
00083         {
00084             /* It's committed, so return the VAD protection */
00085             *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
00086         }
00087         else
00088         {
00089             /* It has not yet been committed, so return no access */
00090             *ProtectCode = MM_NOACCESS;
00091         }
00092         return NULL;
00093     }
00094     else
00095     {
00096         /* Return the proto VAD */
00097         ASSERT(Vad->u2.VadFlags2.ExtendableFile == 0);
00098         *ProtoVad = Vad;
00099 
00100         /* Get the prototype PTE for this page */
00101         PointerPte = (((ULONG_PTR)VirtualAddress >> PAGE_SHIFT) - Vad->StartingVpn) + Vad->FirstPrototypePte;
00102         ASSERT(PointerPte <= Vad->LastContiguousPte);
00103         ASSERT(PointerPte != NULL);
00104 
00105         /* Return the Prototype PTE and the protection for the page mapping */
00106         *ProtectCode = (ULONG)Vad->u.VadFlags.Protection;
00107         return PointerPte;
00108     }
00109 }
00110 
00111 #if (_MI_PAGING_LEVELS == 2)
00112 BOOLEAN
00113 FORCEINLINE
00114 MiSynchronizeSystemPde(PMMPDE PointerPde)
00115 {
00116     MMPDE SystemPde;
00117     ULONG Index;
00118 
00119     /* Get the Index from the PDE */
00120     Index = ((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE);
00121 
00122     /* Copy the PDE from the double-mapped system page directory */
00123     SystemPde = MmSystemPagePtes[Index];
00124     *PointerPde = SystemPde;
00125 
00126     /* Make sure we re-read the PDE and PTE */
00127     KeMemoryBarrierWithoutFence();
00128 
00129     /* Return, if we had success */
00130     return (BOOLEAN)SystemPde.u.Hard.Valid;
00131 }
00132 
00133 NTSTATUS
00134 FASTCALL
00135 MiCheckPdeForPagedPool(IN PVOID Address)
00136 {
00137     PMMPDE PointerPde;
00138     NTSTATUS Status = STATUS_SUCCESS;
00139 
00140     /* No session support in ReactOS yet */
00141     ASSERT(MI_IS_SESSION_ADDRESS(Address) == FALSE);
00142     ASSERT(MI_IS_SESSION_PTE(Address) == FALSE);
00143 
00144     //
00145     // Check if this is a fault while trying to access the page table itself
00146     //
00147     if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
00148     {
00149         //
00150         // Send a hint to the page fault handler that this is only a valid fault
00151         // if we already detected this was access within the page table range
00152         //
00153         PointerPde = (PMMPDE)MiAddressToPte(Address);
00154         Status = STATUS_WAIT_1;
00155     }
00156     else if (Address < MmSystemRangeStart)
00157     {
00158         //
00159         // This is totally illegal
00160         //
00161         return STATUS_ACCESS_VIOLATION;
00162     }
00163     else
00164     {
00165         //
00166         // Get the PDE for the address
00167         //
00168         PointerPde = MiAddressToPde(Address);
00169     }
00170 
00171     //
00172     // Check if it's not valid
00173     //
00174     if (PointerPde->u.Hard.Valid == 0)
00175     {
00176 #ifdef _M_AMD64
00177         ASSERT(FALSE);
00178 #else
00179         //
00180         // Copy it from our double-mapped system page directory
00181         //
00182         InterlockedExchangePte(PointerPde,
00183                                MmSystemPagePtes[((ULONG_PTR)PointerPde & (SYSTEM_PD_SIZE - 1)) / sizeof(MMPTE)].u.Long);
00184 #endif
00185     }
00186 
00187     //
00188     // Return status
00189     //
00190     return Status;
00191 }
00192 #else
00193 NTSTATUS
00194 FASTCALL
00195 MiCheckPdeForPagedPool(IN PVOID Address)
00196 {
00197     return STATUS_ACCESS_VIOLATION;
00198 }
00199 #endif
00200 
00201 VOID
00202 NTAPI
00203 MiZeroPfn(IN PFN_NUMBER PageFrameNumber)
00204 {
00205     PMMPTE ZeroPte;
00206     MMPTE TempPte;
00207     PMMPFN Pfn1;
00208     PVOID ZeroAddress;
00209 
00210     /* Get the PFN for this page */
00211     Pfn1 = MiGetPfnEntry(PageFrameNumber);
00212     ASSERT(Pfn1);
00213 
00214     /* Grab a system PTE we can use to zero the page */
00215     ZeroPte = MiReserveSystemPtes(1, SystemPteSpace);
00216     ASSERT(ZeroPte);
00217 
00218     /* Initialize the PTE for it */
00219     TempPte = ValidKernelPte;
00220     TempPte.u.Hard.PageFrameNumber = PageFrameNumber;
00221 
00222     /* Setup caching */
00223     if (Pfn1->u3.e1.CacheAttribute == MiWriteCombined)
00224     {
00225         /* Write combining, no caching */
00226         MI_PAGE_DISABLE_CACHE(&TempPte);
00227         MI_PAGE_WRITE_COMBINED(&TempPte);
00228     }
00229     else if (Pfn1->u3.e1.CacheAttribute == MiNonCached)
00230     {
00231         /* Write through, no caching */
00232         MI_PAGE_DISABLE_CACHE(&TempPte);
00233         MI_PAGE_WRITE_THROUGH(&TempPte);
00234     }
00235 
00236     /* Make the system PTE valid with our PFN */
00237     MI_WRITE_VALID_PTE(ZeroPte, TempPte);
00238 
00239     /* Get the address it maps to, and zero it out */
00240     ZeroAddress = MiPteToAddress(ZeroPte);
00241     KeZeroPages(ZeroAddress, PAGE_SIZE);
00242 
00243     /* Now get rid of it */
00244     MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
00245 }
00246 
00247 NTSTATUS
00248 NTAPI
00249 MiResolveDemandZeroFault(IN PVOID Address,
00250                          IN PMMPTE PointerPte,
00251                          IN PEPROCESS Process,
00252                          IN KIRQL OldIrql)
00253 {
00254     PFN_NUMBER PageFrameNumber = 0;
00255     MMPTE TempPte;
00256     BOOLEAN NeedZero = FALSE, HaveLock = FALSE;
00257     ULONG Color;
00258     DPRINT("ARM3 Demand Zero Page Fault Handler for address: %p in process: %p\n",
00259             Address,
00260             Process);
00261 
00262     /* Must currently only be called by paging path */
00263     if ((Process) && (OldIrql == MM_NOIRQL))
00264     {
00265         /* Sanity check */
00266         ASSERT(MI_IS_PAGE_TABLE_ADDRESS(PointerPte));
00267 
00268         /* No forking yet */
00269         ASSERT(Process->ForkInProgress == NULL);
00270 
00271         /* Get process color */
00272         Color = MI_GET_NEXT_PROCESS_COLOR(Process);
00273         ASSERT(Color != 0xFFFFFFFF);
00274 
00275         /* We'll need a zero page */
00276         NeedZero = TRUE;
00277     }
00278     else
00279     {
00280         /* Check if we need a zero page */
00281         NeedZero = (OldIrql != MM_NOIRQL);
00282 
00283         /* Get the next system page color */
00284         Color = MI_GET_NEXT_COLOR();
00285     }
00286 
00287     /* Check if the PFN database should be acquired */
00288     if (OldIrql == MM_NOIRQL)
00289     {
00290         /* Acquire it and remember we should release it after */
00291         OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00292         HaveLock = TRUE;
00293     }
00294 
00295     /* We either manually locked the PFN DB, or already came with it locked */
00296     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
00297 
00298     /* Do we need a zero page? */
00299     ASSERT(PointerPte->u.Hard.Valid == 0);
00300 #if MI_TRACE_PFNS
00301     if (UserPdeFault) MI_SET_USAGE(MI_USAGE_PAGE_TABLE);
00302     if (!UserPdeFault) MI_SET_USAGE(MI_USAGE_DEMAND_ZERO);
00303 #endif
00304     if (Process) MI_SET_PROCESS2(Process->ImageFileName);
00305     if (!Process) MI_SET_PROCESS2("Kernel Demand 0");
00306     if ((NeedZero) && (Process))
00307     {
00308         /* Try to get one, if we couldn't grab a free page and zero it */
00309         PageFrameNumber = MiRemoveZeroPageSafe(Color);
00310         if (PageFrameNumber)
00311         {
00312             /* We got a genuine zero page, stop worrying about it */
00313             NeedZero = FALSE;
00314         }
00315         else
00316         {
00317             /* We'll need a free page and zero it manually */
00318             PageFrameNumber = MiRemoveAnyPage(Color);
00319         }
00320     }
00321     else if (!NeedZero)
00322     {
00323         /* Process or system doesn't want a zero page, grab anything */
00324         PageFrameNumber = MiRemoveAnyPage(Color);
00325     }
00326     else
00327     {
00328         /* System wants a zero page, obtain one */
00329         PageFrameNumber = MiRemoveZeroPage(Color);
00330         NeedZero = FALSE;
00331     }
00332 
00333     /* Initialize it */
00334     MiInitializePfn(PageFrameNumber, PointerPte, TRUE);
00335 
00336     /* Increment demand zero faults */
00337     KeGetCurrentPrcb()->MmDemandZeroCount++;
00338 
00339     /* Release PFN lock if needed */
00340     if (HaveLock) KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00341 
00342     /* Zero the page if need be */
00343     if (NeedZero) MiZeroPfn(PageFrameNumber);
00344 
00345     /* Fault on user PDE, or fault on user PTE? */
00346     if (PointerPte <= MiHighestUserPte)
00347     {
00348         /* User fault, build a user PTE */
00349         MI_MAKE_HARDWARE_PTE_USER(&TempPte,
00350                                   PointerPte,
00351                                   PointerPte->u.Soft.Protection,
00352                                   PageFrameNumber);
00353     }
00354     else
00355     {
00356         /* This is a user-mode PDE, create a kernel PTE for it */
00357         MI_MAKE_HARDWARE_PTE(&TempPte,
00358                              PointerPte,
00359                              PointerPte->u.Soft.Protection,
00360                              PageFrameNumber);
00361     }
00362 
00363     /* Set it dirty if it's a writable page */
00364     if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
00365 
00366     /* Write it */
00367     MI_WRITE_VALID_PTE(PointerPte, TempPte);
00368 
00369     //
00370     // It's all good now
00371     //
00372     DPRINT("Demand zero page has now been paged in\n");
00373     return STATUS_PAGE_FAULT_DEMAND_ZERO;
00374 }
00375 
00376 NTSTATUS
00377 NTAPI
00378 MiCompleteProtoPteFault(IN BOOLEAN StoreInstruction,
00379                         IN PVOID Address,
00380                         IN PMMPTE PointerPte,
00381                         IN PMMPTE PointerProtoPte,
00382                         IN KIRQL OldIrql,
00383                         IN PMMPFN Pfn1)
00384 {
00385     MMPTE TempPte;
00386     PMMPTE OriginalPte, PageTablePte;
00387     ULONG_PTR Protection;
00388     PFN_NUMBER PageFrameIndex;
00389     PMMPFN Pfn2;
00390 
00391     /* Must be called with an valid prototype PTE, with the PFN lock held */
00392     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
00393     ASSERT(PointerProtoPte->u.Hard.Valid == 1);
00394 
00395     /* Get the page */
00396     PageFrameIndex = PFN_FROM_PTE(PointerProtoPte);
00397 
00398     /* Get the PFN entry and set it as a prototype PTE */
00399     Pfn1 = MiGetPfnEntry(PageFrameIndex);
00400     Pfn1->u3.e1.PrototypePte = 1;
00401 
00402     /* Increment the share count for the page table */
00403     // FIXME: This doesn't work because we seem to bump the sharecount to two, and MiDeletePte gets annoyed and ASSERTs.
00404     // This could be beause MiDeletePte is now being called from strange code in Rosmm
00405     PageTablePte = MiAddressToPte(PointerPte);
00406     Pfn2 = MiGetPfnEntry(PageTablePte->u.Hard.PageFrameNumber);
00407     //Pfn2->u2.ShareCount++;
00408 
00409     /* Check where we should be getting the protection information from */
00410     if (PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED)
00411     {
00412         /* Get the protection from the PTE, there's no real Proto PTE data */
00413         Protection = PointerPte->u.Soft.Protection;
00414     }
00415     else
00416     {
00417         /* Get the protection from the original PTE link */
00418         OriginalPte = &Pfn1->OriginalPte;
00419         Protection = OriginalPte->u.Soft.Protection;
00420     }
00421 
00422     /* Release the PFN lock */
00423     KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00424 
00425     /* Remove caching bits */
00426     Protection &= ~(MM_NOCACHE | MM_NOACCESS);
00427 
00428     /* Check if this is a kernel or user address */
00429     if (Address < MmSystemRangeStart)
00430     {
00431         /* Build the user PTE */
00432         MI_MAKE_HARDWARE_PTE_USER(&TempPte, PointerPte, Protection, PageFrameIndex);
00433     }
00434     else
00435     {
00436         /* Build the kernel PTE */
00437         MI_MAKE_HARDWARE_PTE(&TempPte, PointerPte, Protection, PageFrameIndex);
00438     }
00439 
00440     /* Write the PTE */
00441     MI_WRITE_VALID_PTE(PointerPte, TempPte);
00442 
00443     /* Return success */
00444     return STATUS_SUCCESS;
00445 }
00446 
00447 NTSTATUS
00448 NTAPI
00449 MiResolveTransitionFault(IN PVOID FaultingAddress,
00450                          IN PMMPTE PointerPte,
00451                          IN PEPROCESS CurrentProcess,
00452                          IN KIRQL OldIrql,
00453                          OUT PVOID *InPageBlock)
00454 {
00455     PFN_NUMBER PageFrameIndex;
00456     PMMPFN Pfn1;
00457     MMPTE TempPte;
00458     PMMPTE PointerToPteForProtoPage;
00459     USHORT NewRefCount;
00460     DPRINT1("Transition fault on 0x%p with PTE 0x%lx in process %s\n", FaultingAddress, PointerPte, CurrentProcess->ImageFileName);
00461 
00462     /* Windowss does this check */
00463     ASSERT(*InPageBlock == NULL);
00464 
00465     /* ARM3 doesn't support this path */
00466     ASSERT(OldIrql != MM_NOIRQL);
00467 
00468     /* Capture the PTE and make sure it's in transition format */
00469     TempPte = *PointerPte;
00470     ASSERT((TempPte.u.Soft.Valid == 0) &&
00471            (TempPte.u.Soft.Prototype == 0) &&
00472            (TempPte.u.Soft.Transition == 1));
00473 
00474     /* Get the PFN and the PFN entry */
00475     PageFrameIndex = TempPte.u.Trans.PageFrameNumber;
00476     DPRINT1("Transition PFN: %lx\n", PageFrameIndex);
00477     Pfn1 = MiGetPfnEntry(PageFrameIndex);
00478 
00479     /* One more transition fault! */
00480     InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
00481 
00482     /* This is from ARM3 -- Windows normally handles this here */
00483     ASSERT(Pfn1->u4.InPageError == 0);
00484 
00485     /* Not supported in ARM3 */
00486     ASSERT(Pfn1->u3.e1.ReadInProgress == 0);
00487 
00488     /* Windows checks there's some free pages and this isn't an in-page error */
00489     ASSERT(MmAvailablePages >= 0);
00490     ASSERT(Pfn1->u4.InPageError == 0);
00491 
00492     /* Was this a transition page in the valid list, or free/zero list? */
00493     if (Pfn1->u3.e1.PageLocation == ActiveAndValid)
00494     {
00495         /* All Windows does here is a bunch of sanity checks */
00496         DPRINT1("Transition in active list\n");
00497         ASSERT((Pfn1->PteAddress >= MiAddressToPte(MmPagedPoolStart)) &&
00498                (Pfn1->PteAddress <= MiAddressToPte(MmPagedPoolEnd)));
00499         ASSERT(Pfn1->u2.ShareCount != 0);
00500         ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
00501     }
00502     else
00503     {
00504         /* Otherwise, the page is removed from its list */
00505         DPRINT1("Transition page in free/zero list\n");
00506         MiUnlinkPageFromList(Pfn1);
00507 
00508         /* Windows does these checks -- perhaps a macro? */
00509         ASSERT(Pfn1->u2.ShareCount == 0);
00510         ASSERT(Pfn1->u2.ShareCount == 0);
00511         ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
00512 
00513         /* Check if this was a prototype PTE */
00514         if ((Pfn1->u3.e1.PrototypePte == 1) &&
00515             (Pfn1->OriginalPte.u.Soft.Prototype == 1))
00516         {
00517             DPRINT1("Prototype floating page not yet supported\n");
00518             ASSERT(FALSE);
00519         }
00520 
00521         /* FIXME: Update counter */
00522 
00523         /* We must be the first reference */
00524         NewRefCount = InterlockedIncrement16((PSHORT)&Pfn1->u3.e2.ReferenceCount);
00525         ASSERT(NewRefCount == 1);
00526     }
00527 
00528     /* At this point, there should no longer be any in-page errors */
00529     ASSERT(Pfn1->u4.InPageError == 0);
00530 
00531     /* Check if this was a PFN with no more share references */
00532     if (Pfn1->u2.ShareCount == 0)
00533     {
00534         /* Windows checks for these... maybe a macro? */
00535         ASSERT(Pfn1->u3.e2.ReferenceCount != 0);
00536         ASSERT(Pfn1->u2.ShareCount == 0);
00537 
00538         /* Was this the last active reference to it */
00539         DPRINT1("Page share count is zero\n");
00540         if (Pfn1->u3.e2.ReferenceCount == 1)
00541         {
00542             /* The page should be leaking somewhere on the free/zero list */
00543             DPRINT1("Page reference count is one\n");
00544             ASSERT(Pfn1->u3.e1.PageLocation != ActiveAndValid);
00545             if ((Pfn1->u3.e1.PrototypePte == 1) &&
00546                 (Pfn1->OriginalPte.u.Soft.Prototype == 1))
00547             {
00548                 /* Do extra processing if it was a prototype page */
00549                 DPRINT1("Prototype floating page not yet supported\n");
00550                 ASSERT(FALSE);
00551             }
00552 
00553             /* FIXME: Update counter */
00554         }
00555     }
00556 
00557     /* Bump the share count and make the page valid */
00558     Pfn1->u2.ShareCount++;
00559     Pfn1->u3.e1.PageLocation = ActiveAndValid;
00560 
00561     /* Prototype PTEs are in paged pool, which itself might be in transition */
00562     if (FaultingAddress >= MmSystemRangeStart)
00563     {
00564         /* Check if this is a paged pool PTE in transition state */
00565         PointerToPteForProtoPage = MiAddressToPte(PointerPte);
00566         TempPte = *PointerToPteForProtoPage;
00567         if ((TempPte.u.Hard.Valid == 0) && (TempPte.u.Soft.Transition == 1))
00568         {
00569             /* This isn't yet supported */
00570             DPRINT1("Double transition fault not yet supported\n");
00571             ASSERT(FALSE);
00572         }
00573     }
00574 
00575     /* Build the transition PTE -- maybe a macro? */
00576     ASSERT(PointerPte->u.Hard.Valid == 0);
00577     ASSERT(PointerPte->u.Trans.Prototype == 0);
00578     ASSERT(PointerPte->u.Trans.Transition == 1);
00579     TempPte.u.Long = (PointerPte->u.Long & ~0xFFF) |
00580                      (MmProtectToPteMask[PointerPte->u.Trans.Protection]) |
00581                      MiDetermineUserGlobalPteMask(PointerPte);
00582 
00583     /* FIXME: Set dirty bit */
00584 
00585     /* Write the valid PTE */
00586     MI_WRITE_VALID_PTE(PointerPte, TempPte);
00587 
00588     /* Return success */
00589     return STATUS_PAGE_FAULT_TRANSITION;
00590 }
00591 
00592 NTSTATUS
00593 NTAPI
00594 MiResolveProtoPteFault(IN BOOLEAN StoreInstruction,
00595                        IN PVOID Address,
00596                        IN PMMPTE PointerPte,
00597                        IN PMMPTE PointerProtoPte,
00598                        IN OUT PMMPFN *OutPfn,
00599                        OUT PVOID *PageFileData,
00600                        OUT PMMPTE PteValue,
00601                        IN PEPROCESS Process,
00602                        IN KIRQL OldIrql,
00603                        IN PVOID TrapInformation)
00604 {
00605     MMPTE TempPte, PteContents;
00606     PMMPFN Pfn1;
00607     PFN_NUMBER PageFrameIndex;
00608     NTSTATUS Status;
00609     PVOID InPageBlock = NULL;
00610 
00611     /* Must be called with an invalid, prototype PTE, with the PFN lock held */
00612     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
00613     ASSERT(PointerPte->u.Hard.Valid == 0);
00614     ASSERT(PointerPte->u.Soft.Prototype == 1);
00615 
00616     /* Read the prototype PTE and check if it's valid */
00617     TempPte = *PointerProtoPte;
00618     if (TempPte.u.Hard.Valid == 1)
00619     {
00620         /* One more user of this mapped page */
00621         PageFrameIndex = PFN_FROM_PTE(&TempPte);
00622         Pfn1 = MiGetPfnEntry(PageFrameIndex);
00623         Pfn1->u2.ShareCount++;
00624 
00625         /* Call it a transition */
00626         InterlockedIncrement(&KeGetCurrentPrcb()->MmTransitionCount);
00627 
00628         /* Complete the prototype PTE fault -- this will release the PFN lock */
00629         return MiCompleteProtoPteFault(StoreInstruction,
00630                                        Address,
00631                                        PointerPte,
00632                                        PointerProtoPte,
00633                                        OldIrql,
00634                                        NULL);
00635     }
00636 
00637     /* Make sure there's some protection mask */
00638     if (TempPte.u.Long == 0)
00639     {
00640         /* Release the lock */
00641         DPRINT1("Access on reserved section?\n");
00642         KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00643         return STATUS_ACCESS_VIOLATION;
00644     }
00645 
00646     /* Check for access rights on the PTE proper */
00647     PteContents = *PointerPte;
00648     if (PteContents.u.Soft.PageFileHigh != MI_PTE_LOOKUP_NEEDED)
00649     {
00650         if (!PteContents.u.Proto.ReadOnly)
00651         {
00652             /* FIXME: CHECK FOR ACCESS AND COW */
00653         }
00654     }
00655     else
00656     {
00657         /* FIXME: Should check for COW */
00658     }
00659 
00660     /* Check for clone PTEs */
00661     if (PointerPte <= MiHighestUserPte) ASSERT(Process->CloneRoot == NULL);
00662 
00663     /* We don't support mapped files yet */
00664     ASSERT(TempPte.u.Soft.Prototype == 0);
00665 
00666     /* We might however have transition PTEs */
00667     if (TempPte.u.Soft.Transition == 1)
00668     {
00669         /* Resolve the transition fault */
00670         ASSERT(OldIrql != MM_NOIRQL);
00671         Status = MiResolveTransitionFault(Address,
00672                                           PointerProtoPte,
00673                                           Process,
00674                                           OldIrql,
00675                                           &InPageBlock);
00676         ASSERT(NT_SUCCESS(Status));
00677     }
00678     else
00679     {
00680         /* We also don't support paged out pages */
00681         ASSERT(TempPte.u.Soft.PageFileHigh == 0);
00682 
00683         /* Resolve the demand zero fault */
00684         Status = MiResolveDemandZeroFault(Address,
00685                                           PointerProtoPte,
00686                                           Process,
00687                                           OldIrql);
00688         ASSERT(NT_SUCCESS(Status));
00689     }
00690 
00691     /* Complete the prototype PTE fault -- this will release the PFN lock */
00692     ASSERT(PointerPte->u.Hard.Valid == 0);
00693     return MiCompleteProtoPteFault(StoreInstruction,
00694                                    Address,
00695                                    PointerPte,
00696                                    PointerProtoPte,
00697                                    OldIrql,
00698                                    NULL);
00699 }
00700 
00701 NTSTATUS
00702 NTAPI
00703 MiDispatchFault(IN BOOLEAN StoreInstruction,
00704                 IN PVOID Address,
00705                 IN PMMPTE PointerPte,
00706                 IN PMMPTE PointerProtoPte,
00707                 IN BOOLEAN Recursive,
00708                 IN PEPROCESS Process,
00709                 IN PVOID TrapInformation,
00710                 IN PVOID Vad)
00711 {
00712     MMPTE TempPte;
00713     KIRQL OldIrql, LockIrql;
00714     NTSTATUS Status;
00715     PMMPTE SuperProtoPte;
00716     DPRINT("ARM3 Page Fault Dispatcher for address: %p in process: %p\n",
00717              Address,
00718              Process);
00719 
00720     /* Make sure the addresses are ok */
00721     ASSERT(PointerPte == MiAddressToPte(Address));
00722 
00723     //
00724     // Make sure APCs are off and we're not at dispatch
00725     //
00726     OldIrql = KeGetCurrentIrql();
00727     ASSERT(OldIrql <= APC_LEVEL);
00728     ASSERT(KeAreAllApcsDisabled() == TRUE);
00729 
00730     //
00731     // Grab a copy of the PTE
00732     //
00733     TempPte = *PointerPte;
00734 
00735     /* Do we have a prototype PTE? */
00736     if (PointerProtoPte)
00737     {
00738         /* This should never happen */
00739         ASSERT(!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
00740 
00741         /* Check if this is a kernel-mode address */
00742         SuperProtoPte = MiAddressToPte(PointerProtoPte);
00743         if (Address >= MmSystemRangeStart)
00744         {
00745             /* Lock the PFN database */
00746             LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00747 
00748             /* Has the PTE been made valid yet? */
00749             if (!SuperProtoPte->u.Hard.Valid)
00750             {
00751                 UNIMPLEMENTED;
00752                 while (TRUE);
00753             }
00754             else
00755             {
00756                 /* Resolve the fault -- this will release the PFN lock */
00757                 ASSERT(PointerPte->u.Hard.Valid == 0);
00758                 Status = MiResolveProtoPteFault(StoreInstruction,
00759                                                 Address,
00760                                                 PointerPte,
00761                                                 PointerProtoPte,
00762                                                 NULL,
00763                                                 NULL,
00764                                                 NULL,
00765                                                 Process,
00766                                                 LockIrql,
00767                                                 TrapInformation);
00768                 ASSERT(Status == STATUS_SUCCESS);
00769 
00770                 /* Complete this as a transition fault */
00771                 ASSERT(OldIrql == KeGetCurrentIrql());
00772                 ASSERT(OldIrql <= APC_LEVEL);
00773                 ASSERT(KeAreAllApcsDisabled() == TRUE);
00774                 return Status;
00775             }
00776         }
00777         else
00778         {
00779             /* We currently only handle very limited paths */
00780             ASSERT(PointerPte->u.Soft.Prototype == 1);
00781             ASSERT(PointerPte->u.Soft.PageFileHigh == MI_PTE_LOOKUP_NEEDED);
00782 
00783             /* Lock the PFN database */
00784             LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00785 
00786             /* For our current usage, this should be true */
00787             ASSERT(SuperProtoPte->u.Hard.Valid == 1);
00788             ASSERT(TempPte.u.Hard.Valid == 0);
00789 
00790             /* Resolve the fault -- this will release the PFN lock */
00791             Status = MiResolveProtoPteFault(StoreInstruction,
00792                                             Address,
00793                                             PointerPte,
00794                                             PointerProtoPte,
00795                                             NULL,
00796                                             NULL,
00797                                             NULL,
00798                                             Process,
00799                                             LockIrql,
00800                                             TrapInformation);
00801             ASSERT(Status == STATUS_SUCCESS);
00802 
00803             /* Complete this as a transition fault */
00804             ASSERT(OldIrql == KeGetCurrentIrql());
00805             ASSERT(OldIrql <= APC_LEVEL);
00806             ASSERT(KeAreAllApcsDisabled() == TRUE);
00807             return STATUS_PAGE_FAULT_TRANSITION;
00808         }
00809     }
00810 
00811     //
00812     // The PTE must be invalid but not completely empty. It must also not be a
00813     // prototype PTE as that scenario should've been handled above
00814     //
00815     ASSERT(TempPte.u.Hard.Valid == 0);
00816     ASSERT(TempPte.u.Soft.Prototype == 0);
00817     ASSERT(TempPte.u.Long != 0);
00818 
00819     //
00820     // No transition or page file software PTEs in ARM3 yet, so this must be a
00821     // demand zero page
00822     //
00823     ASSERT(TempPte.u.Soft.Transition == 0);
00824     ASSERT(TempPte.u.Soft.PageFileHigh == 0);
00825 
00826     //
00827     // If we got this far, the PTE can only be a demand zero PTE, which is what
00828     // we want. Go handle it!
00829     //
00830     Status = MiResolveDemandZeroFault(Address,
00831                                       PointerPte,
00832                                       Process,
00833                                       MM_NOIRQL);
00834     ASSERT(KeAreAllApcsDisabled() == TRUE);
00835     if (NT_SUCCESS(Status))
00836     {
00837         //
00838         // Make sure we're returning in a sane state and pass the status down
00839         //
00840         ASSERT(OldIrql == KeGetCurrentIrql());
00841         ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
00842         return Status;
00843     }
00844 
00845     //
00846     // Generate an access fault
00847     //
00848     return STATUS_ACCESS_VIOLATION;
00849 }
00850 
00851 NTSTATUS
00852 NTAPI
00853 MmArmAccessFault(IN BOOLEAN StoreInstruction,
00854                  IN PVOID Address,
00855                  IN KPROCESSOR_MODE Mode,
00856                  IN PVOID TrapInformation)
00857 {
00858     KIRQL OldIrql = KeGetCurrentIrql(), LockIrql;
00859     PMMPTE ProtoPte = NULL;
00860     PMMPTE PointerPte = MiAddressToPte(Address);
00861     PMMPDE PointerPde = MiAddressToPde(Address);
00862 #if (_MI_PAGING_LEVELS >= 3)
00863     PMMPDE PointerPpe = MiAddressToPpe(Address);
00864 #if (_MI_PAGING_LEVELS == 4)
00865     PMMPDE PointerPxe = MiAddressToPxe(Address);
00866 #endif
00867 #endif
00868     MMPTE TempPte;
00869     PETHREAD CurrentThread;
00870     PEPROCESS CurrentProcess;
00871     NTSTATUS Status;
00872     PMMSUPPORT WorkingSet;
00873     ULONG ProtectionCode;
00874     PMMVAD Vad;
00875     PFN_NUMBER PageFrameIndex;
00876     ULONG Color;
00877     DPRINT("ARM3 FAULT AT: %p\n", Address);
00878 
00879     /* Check for page fault on high IRQL */
00880     if (OldIrql > APC_LEVEL)
00881     {
00882         // There are some special cases where this is okay, but not in ARM3 yet
00883         DbgPrint("MM:***PAGE FAULT AT IRQL > 1  Va %p, IRQL %lx\n",
00884                  Address,
00885                  OldIrql);
00886         ASSERT(OldIrql <= APC_LEVEL);
00887     }
00888 
00889     /* Check for kernel fault address */
00890     if (Address >= MmSystemRangeStart)
00891     {
00892         /* Bail out, if the fault came from user mode */
00893         if (Mode == UserMode) return STATUS_ACCESS_VIOLATION;
00894 
00895         /* PXEs and PPEs for kernel mode are mapped for everything we need */
00896 #if (_MI_PAGING_LEVELS >= 3)
00897         if (
00898 #if (_MI_PAGING_LEVELS == 4)
00899             (PointerPxe->u.Hard.Valid == 0) ||
00900 #endif
00901             (PointerPpe->u.Hard.Valid == 0))
00902         {
00903             /* The address is not from any pageable area! */
00904             KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
00905                          (ULONG_PTR)Address,
00906                          StoreInstruction,
00907                          (ULONG_PTR)TrapInformation,
00908                          2);
00909         }
00910 #endif
00911 
00912 #if (_MI_PAGING_LEVELS == 2)
00913         /* Check if we have a situation that might need synchronization
00914            of the PDE with the system page directory */
00915         if (MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address))
00916         {
00917             /* This could be a paged pool commit with an unsychronized PDE.
00918                NOTE: This way it works on x86, verify for other architectures! */
00919             if (MiSynchronizeSystemPde((PMMPDE)PointerPte)) return STATUS_SUCCESS;
00920         }
00921 #endif
00922 
00923         /* Check if the PDE is invalid */
00924         if (PointerPde->u.Hard.Valid == 0)
00925         {
00926 #if (_MI_PAGING_LEVELS == 2)
00927             /* Sync this PDE and check, if that made it valid */
00928             if (!MiSynchronizeSystemPde(PointerPde))
00929 #endif
00930             {
00931                 /* PDE (still) not valid, kill the system */
00932                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
00933                              (ULONG_PTR)Address,
00934                              StoreInstruction,
00935                              (ULONG_PTR)TrapInformation,
00936                              2);
00937             }
00938         }
00939 
00940         /* The PDE is valid, so read the PTE */
00941         TempPte = *PointerPte;
00942         if (TempPte.u.Hard.Valid == 1)
00943         {
00944             //
00945             // Only two things can go wrong here:
00946             // Executing NX page (we couldn't care less)
00947             // Writing to a read-only page (the stuff ARM3 works with is write,
00948             // so again, moot point).
00949             //
00950 
00951             //
00952             // Otherwise, the PDE was probably invalid, and all is good now
00953             //
00954             return STATUS_SUCCESS;
00955         }
00956 
00957         /* Get the current thread */
00958         CurrentThread = PsGetCurrentThread();
00959 
00960         /* Check for a fault on the page table or hyperspace */
00961         if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address)) goto UserFault;
00962 
00963         /* Use the system working set */
00964         WorkingSet = &MmSystemCacheWs;
00965         CurrentProcess = NULL;
00966 
00967         /* Acquire the working set lock */
00968         KeRaiseIrql(APC_LEVEL, &LockIrql);
00969         MiLockWorkingSet(CurrentThread, WorkingSet);
00970 
00971         /* Re-read PTE now that we own the lock */
00972         TempPte = *PointerPte;
00973         if (TempPte.u.Hard.Valid == 1)
00974         {
00975             // Only two things can go wrong here:
00976             // Executing NX page (we couldn't care less)
00977             // Writing to a read-only page (the stuff ARM3 works with is write,
00978             // so again, moot point).
00979             ASSERT(TempPte.u.Hard.Write == 1);
00980 
00981             /* Release the working set */
00982             MiUnlockWorkingSet(CurrentThread, WorkingSet);
00983             KeLowerIrql(LockIrql);
00984 
00985             // Otherwise, the PDE was probably invalid, and all is good now
00986             return STATUS_SUCCESS;
00987         }
00988 
00989         /* Check one kind of prototype PTE */
00990         if (TempPte.u.Soft.Prototype)
00991         {
00992             /* Make sure protected pool is on, and that this is a pool address */
00993             if ((MmProtectFreedNonPagedPool) &&
00994                 (((Address >= MmNonPagedPoolStart) &&
00995                   (Address < (PVOID)((ULONG_PTR)MmNonPagedPoolStart +
00996                                      MmSizeOfNonPagedPoolInBytes))) ||
00997                  ((Address >= MmNonPagedPoolExpansionStart) &&
00998                   (Address < MmNonPagedPoolEnd))))
00999             {
01000                 /* Bad boy, bad boy, whatcha gonna do, whatcha gonna do when ARM3 comes for you! */
01001                 KeBugCheckEx(DRIVER_CAUGHT_MODIFYING_FREED_POOL,
01002                              (ULONG_PTR)Address,
01003                              StoreInstruction,
01004                              Mode,
01005                              4);
01006             }
01007 
01008             /* Get the prototype PTE! */
01009             ProtoPte = MiProtoPteToPte(&TempPte);
01010         }
01011         else
01012         {
01013             /* We don't implement transition PTEs */
01014             ASSERT(TempPte.u.Soft.Transition == 0);
01015 
01016             /* Check for no-access PTE */
01017             if (TempPte.u.Soft.Protection == MM_NOACCESS)
01018             {
01019                 /* Bugcheck the system! */
01020                 KeBugCheckEx(PAGE_FAULT_IN_NONPAGED_AREA,
01021                              (ULONG_PTR)Address,
01022                              StoreInstruction,
01023                              (ULONG_PTR)TrapInformation,
01024                              1);
01025             }
01026 
01027             /* Check for demand page */
01028             if ((StoreInstruction) && !(TempPte.u.Hard.Valid))
01029             {
01030                 /* Get the protection code */
01031                 if (!(TempPte.u.Soft.Protection & MM_READWRITE))
01032                 {
01033                     /* Bugcheck the system! */
01034                     KeBugCheckEx(ATTEMPTED_WRITE_TO_READONLY_MEMORY,
01035                                  (ULONG_PTR)Address,
01036                                  TempPte.u.Long,
01037                                  (ULONG_PTR)TrapInformation,
01038                                  14);
01039                 }
01040             }
01041         }
01042 
01043         /* Now do the real fault handling */
01044         Status = MiDispatchFault(StoreInstruction,
01045                                  Address,
01046                                  PointerPte,
01047                                  ProtoPte,
01048                                  FALSE,
01049                                  CurrentProcess,
01050                                  TrapInformation,
01051                                  NULL);
01052 
01053         /* Release the working set */
01054         ASSERT(KeAreAllApcsDisabled() == TRUE);
01055         MiUnlockWorkingSet(CurrentThread, WorkingSet);
01056         KeLowerIrql(LockIrql);
01057 
01058         /* We are done! */
01059         DPRINT("Fault resolved with status: %lx\n", Status);
01060         return Status;
01061     }
01062 
01063     /* This is a user fault */
01064 UserFault:
01065     CurrentThread = PsGetCurrentThread();
01066     CurrentProcess = (PEPROCESS)CurrentThread->Tcb.ApcState.Process;
01067 
01068     /* Lock the working set */
01069     MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
01070 
01071 #if (_MI_PAGING_LEVELS == 2)
01072     ASSERT(PointerPde->u.Hard.LargePage == 0);
01073 #endif
01074 
01075 #if (_MI_PAGING_LEVELS == 4)
01076 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
01077 // also this is missing the page count increment
01078     /* Check if the PXE is valid */
01079     if (PointerPxe->u.Hard.Valid == 0)
01080     {
01081         /* Right now, we only handle scenarios where the PXE is totally empty */
01082         ASSERT(PointerPxe->u.Long == 0);
01083 #if 0
01084         /* Resolve a demand zero fault */
01085         Status = MiResolveDemandZeroFault(PointerPpe,
01086                                           MM_READWRITE,
01087                                           CurrentProcess,
01088                                           MM_NOIRQL);
01089 #endif
01090         /* We should come back with a valid PXE */
01091         ASSERT(PointerPxe->u.Hard.Valid == 1);
01092     }
01093 #endif
01094 
01095 #if (_MI_PAGING_LEVELS >= 3)
01096 // Note to Timo: You should call MiCheckVirtualAddress and also check if it's zero pte
01097 // also this is missing the page count increment
01098     /* Check if the PPE is valid */
01099     if (PointerPpe->u.Hard.Valid == 0)
01100     {
01101         /* Right now, we only handle scenarios where the PPE is totally empty */
01102         ASSERT(PointerPpe->u.Long == 0);
01103 #if 0
01104         /* Resolve a demand zero fault */
01105         Status = MiResolveDemandZeroFault(PointerPde,
01106                                           MM_READWRITE,
01107                                           CurrentProcess,
01108                                           MM_NOIRQL);
01109 #endif
01110         /* We should come back with a valid PPE */
01111         ASSERT(PointerPpe->u.Hard.Valid == 1);
01112     }
01113 #endif
01114 
01115     /* Check if the PDE is valid */
01116     if (PointerPde->u.Hard.Valid == 0)
01117     {
01118         /* Right now, we only handle scenarios where the PDE is totally empty */
01119         ASSERT(PointerPde->u.Long == 0);
01120 
01121         /* And go dispatch the fault on the PDE. This should handle the demand-zero */
01122 #if MI_TRACE_PFNS
01123         UserPdeFault = TRUE;
01124 #endif
01125         MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
01126         if (ProtectionCode == MM_NOACCESS)
01127         {
01128 #if (_MI_PAGING_LEVELS == 2)
01129             /* Could be a page table for paged pool */
01130             MiCheckPdeForPagedPool(Address);
01131 #endif
01132             /* Has the code above changed anything -- is this now a valid PTE? */
01133             Status = (PointerPde->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
01134 
01135             /* Either this was a bogus VA or we've fixed up a paged pool PDE */
01136             MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
01137             return Status;
01138         }
01139 
01140         /* Write a demand-zero PDE */
01141         MI_WRITE_INVALID_PTE(PointerPde, DemandZeroPde);
01142 
01143         /* Dispatch the fault */
01144         Status = MiDispatchFault(TRUE,
01145                                  PointerPte,
01146                                  PointerPde,
01147                                  NULL,
01148                                  FALSE,
01149                                  PsGetCurrentProcess(),
01150                                  TrapInformation,
01151                                  NULL);
01152 #if MI_TRACE_PFNS
01153         UserPdeFault = FALSE;
01154 #endif
01155         /* We should come back with APCs enabled, and with a valid PDE */
01156         ASSERT(KeAreAllApcsDisabled() == TRUE);
01157         ASSERT(PointerPde->u.Hard.Valid == 1);
01158     }
01159 
01160     /* Now capture the PTE. Ignore virtual faults for now */
01161     TempPte = *PointerPte;
01162     ASSERT(TempPte.u.Hard.Valid == 0);
01163 
01164     /* Quick check for demand-zero */
01165     if (TempPte.u.Long == (MM_READWRITE << MM_PTE_SOFTWARE_PROTECTION_BITS))
01166     {
01167         /* Resolve the fault */
01168         MiResolveDemandZeroFault(Address,
01169                                  PointerPte,
01170                                  CurrentProcess,
01171                                  MM_NOIRQL);
01172 
01173         /* Return the status */
01174         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
01175         return STATUS_PAGE_FAULT_DEMAND_ZERO;
01176     }
01177 
01178     /* Make sure it's not a prototype PTE */
01179     ASSERT(TempPte.u.Soft.Prototype == 0);
01180 
01181     /* Check if this address range belongs to a valid allocation (VAD) */
01182     ProtoPte = MiCheckVirtualAddress(Address, &ProtectionCode, &Vad);
01183     if (ProtectionCode == MM_NOACCESS)
01184     {
01185 #if (_MI_PAGING_LEVELS == 2)
01186         /* Could be a page table for paged pool */
01187         MiCheckPdeForPagedPool(Address);
01188 #endif
01189         /* Has the code above changed anything -- is this now a valid PTE? */
01190         Status = (PointerPte->u.Hard.Valid == 1) ? STATUS_SUCCESS : STATUS_ACCESS_VIOLATION;
01191 
01192         /* Either this was a bogus VA or we've fixed up a paged pool PDE */
01193         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
01194         return Status;
01195     }
01196 
01197     /* Check for non-demand zero PTE */
01198     if (TempPte.u.Long != 0)
01199     {
01200         /* This is a page fault */
01201 
01202         /* FIXME: Run MiAccessCheck */
01203 
01204         /* Dispatch the fault */
01205         Status = MiDispatchFault(StoreInstruction,
01206                                  Address,
01207                                  PointerPte,
01208                                  NULL,
01209                                  FALSE,
01210                                  PsGetCurrentProcess(),
01211                                  TrapInformation,
01212                                  NULL);
01213 
01214         /* Return the status */
01215         ASSERT(NT_SUCCESS(Status));
01216         ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
01217         MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
01218         return Status;
01219     }
01220 
01221     /*
01222      * Check if this is a real user-mode address or actually a kernel-mode
01223      * page table for a user mode address
01224      */
01225     if (Address <= MM_HIGHEST_USER_ADDRESS)
01226     {
01227         /* Add an additional page table reference */
01228         MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)]++;
01229         ASSERT(MmWorkingSetList->UsedPageTableEntries[MiGetPdeOffset(Address)] <= PTE_COUNT);
01230     }
01231 
01232     /* No guard page support yet */
01233     ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
01234 
01235     /* Did we get a prototype PTE back? */
01236     if (!ProtoPte)
01237     {
01238         /* Is this PTE actually part of the PDE-PTE self-mapping directory? */
01239         if (PointerPde == MiAddressToPde(PTE_BASE))
01240         {
01241             /* Then it's really a demand-zero PDE (on behalf of user-mode) */
01242             MI_WRITE_INVALID_PTE(PointerPte, DemandZeroPde);
01243         }
01244         else
01245         {
01246             /* No, create a new PTE. First, write the protection */
01247             PointerPte->u.Soft.Protection = ProtectionCode;
01248         }
01249 
01250         /* Lock the PFN database since we're going to grab a page */
01251         OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
01252 
01253         /* Try to get a zero page */
01254         MI_SET_USAGE(MI_USAGE_PEB_TEB);
01255         MI_SET_PROCESS2(CurrentProcess->ImageFileName);
01256         Color = MI_GET_NEXT_PROCESS_COLOR(CurrentProcess);
01257         PageFrameIndex = MiRemoveZeroPageSafe(Color);
01258         if (!PageFrameIndex)
01259         {
01260             /* Grab a page out of there. Later we should grab a colored zero page */
01261             PageFrameIndex = MiRemoveAnyPage(Color);
01262             ASSERT(PageFrameIndex);
01263 
01264             /* Release the lock since we need to do some zeroing */
01265             KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
01266 
01267             /* Zero out the page, since it's for user-mode */
01268             MiZeroPfn(PageFrameIndex);
01269 
01270             /* Grab the lock again so we can initialize the PFN entry */
01271             OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
01272         }
01273 
01274         /* Initialize the PFN entry now */
01275         MiInitializePfn(PageFrameIndex, PointerPte, 1);
01276 
01277         /* One more demand-zero fault */
01278         KeGetCurrentPrcb()->MmDemandZeroCount++;
01279 
01280         /* And we're done with the lock */
01281         KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
01282 
01283         /* Fault on user PDE, or fault on user PTE? */
01284         if (PointerPte <= MiHighestUserPte)
01285         {
01286             /* User fault, build a user PTE */
01287             MI_MAKE_HARDWARE_PTE_USER(&TempPte,
01288                                       PointerPte,
01289                                       PointerPte->u.Soft.Protection,
01290                                       PageFrameIndex);
01291         }
01292         else
01293         {
01294             /* This is a user-mode PDE, create a kernel PTE for it */
01295             MI_MAKE_HARDWARE_PTE(&TempPte,
01296                                  PointerPte,
01297                                  PointerPte->u.Soft.Protection,
01298                                  PageFrameIndex);
01299         }
01300 
01301         /* Write the dirty bit for writeable pages */
01302         if (MI_IS_PAGE_WRITEABLE(&TempPte)) MI_MAKE_DIRTY_PAGE(&TempPte);
01303 
01304         /* And now write down the PTE, making the address valid */
01305         MI_WRITE_VALID_PTE(PointerPte, TempPte);
01306         ASSERT(MiGetPfnEntry(PageFrameIndex)->u1.Event == NULL);
01307 
01308         /* Demand zero */
01309         Status = STATUS_PAGE_FAULT_DEMAND_ZERO;
01310     }
01311     else
01312     {
01313         /* No guard page support yet */
01314         ASSERT((ProtectionCode & MM_DECOMMIT) == 0);
01315         ASSERT(ProtectionCode != 0x100);
01316 
01317         /* Write the prototype PTE */
01318         TempPte = PrototypePte;
01319         TempPte.u.Soft.Protection = ProtectionCode;
01320         MI_WRITE_INVALID_PTE(PointerPte, TempPte);
01321 
01322         /* Handle the fault */
01323         Status = MiDispatchFault(StoreInstruction,
01324                                  Address,
01325                                  PointerPte,
01326                                  ProtoPte,
01327                                  FALSE,
01328                                  CurrentProcess,
01329                                  TrapInformation,
01330                                  Vad);
01331         ASSERT(Status == STATUS_PAGE_FAULT_TRANSITION);
01332         ASSERT(PointerPte->u.Hard.Valid == 1);
01333         ASSERT(PointerPte->u.Hard.PageFrameNumber != 0);
01334     }
01335 
01336     /* Release the working set */
01337     MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
01338     return Status;
01339 }
01340 
01341 NTSTATUS
01342 NTAPI
01343 MmGetExecuteOptions(IN PULONG ExecuteOptions)
01344 {
01345     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
01346     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
01347 
01348     *ExecuteOptions = 0;
01349 
01350     if (CurrentProcess->Flags.ExecuteDisable)
01351     {
01352         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE;
01353     }
01354 
01355     if (CurrentProcess->Flags.ExecuteEnable)
01356     {
01357         *ExecuteOptions |= MEM_EXECUTE_OPTION_ENABLE;
01358     }
01359 
01360     if (CurrentProcess->Flags.DisableThunkEmulation)
01361     {
01362         *ExecuteOptions |= MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION;
01363     }
01364 
01365     if (CurrentProcess->Flags.Permanent)
01366     {
01367         *ExecuteOptions |= MEM_EXECUTE_OPTION_PERMANENT;
01368     }
01369 
01370     if (CurrentProcess->Flags.ExecuteDispatchEnable)
01371     {
01372         *ExecuteOptions |= MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE;
01373     }
01374 
01375     if (CurrentProcess->Flags.ImageDispatchEnable)
01376     {
01377         *ExecuteOptions |= MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE;
01378     }
01379 
01380     return STATUS_SUCCESS;
01381 }
01382 
01383 NTSTATUS
01384 NTAPI
01385 MmSetExecuteOptions(IN ULONG ExecuteOptions)
01386 {
01387     PKPROCESS CurrentProcess = &PsGetCurrentProcess()->Pcb;
01388     KLOCK_QUEUE_HANDLE ProcessLock;
01389     NTSTATUS Status = STATUS_ACCESS_DENIED;
01390     ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
01391 
01392     /* Only accept valid flags */
01393     if (ExecuteOptions & ~MEM_EXECUTE_OPTION_VALID_FLAGS)
01394     {
01395         /* Fail */
01396         DPRINT1("Invalid no-execute options\n");
01397         return STATUS_INVALID_PARAMETER;
01398     }
01399 
01400     /* Change the NX state in the process lock */
01401     KiAcquireProcessLock(CurrentProcess, &ProcessLock);
01402 
01403     /* Don't change anything if the permanent flag was set */
01404     if (!CurrentProcess->Flags.Permanent)
01405     {
01406         /* Start by assuming it's not disabled */
01407         CurrentProcess->Flags.ExecuteDisable = FALSE;
01408 
01409         /* Now process each flag and turn the equivalent bit on */
01410         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE)
01411         {
01412             CurrentProcess->Flags.ExecuteDisable = TRUE;
01413         }
01414         if (ExecuteOptions & MEM_EXECUTE_OPTION_ENABLE)
01415         {
01416             CurrentProcess->Flags.ExecuteEnable = TRUE;
01417         }
01418         if (ExecuteOptions & MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION)
01419         {
01420             CurrentProcess->Flags.DisableThunkEmulation = TRUE;
01421         }
01422         if (ExecuteOptions & MEM_EXECUTE_OPTION_PERMANENT)
01423         {
01424             CurrentProcess->Flags.Permanent = TRUE;
01425         }
01426         if (ExecuteOptions & MEM_EXECUTE_OPTION_EXECUTE_DISPATCH_ENABLE)
01427         {
01428             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
01429         }
01430         if (ExecuteOptions & MEM_EXECUTE_OPTION_IMAGE_DISPATCH_ENABLE)
01431         {
01432             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
01433         }
01434 
01435         /* These are turned on by default if no-execution is also eanbled */
01436         if (CurrentProcess->Flags.ExecuteEnable)
01437         {
01438             CurrentProcess->Flags.ExecuteDispatchEnable = TRUE;
01439             CurrentProcess->Flags.ImageDispatchEnable = TRUE;
01440         }
01441 
01442         /* All good */
01443         Status = STATUS_SUCCESS;
01444     }
01445 
01446     /* Release the lock and return status */
01447     KiReleaseProcessLock(&ProcessLock);
01448     return Status;
01449 }
01450 
01451 /* EOF */

Generated on Sat May 26 2012 04:36:22 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.