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

fault.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00017  *
00018  *
00019  * PROJECT:         ReactOS kernel
00020  * FILE:            ntoskrnl/mm/section/fault.c
00021  * PURPOSE:         Consolidate fault handlers for sections
00022  *
00023  * PROGRAMMERS:     Arty
00024  *                  Rex Jolliff
00025  *                  David Welch
00026  *                  Eric Kohl
00027  *                  Emanuele Aliberti
00028  *                  Eugene Ingerman
00029  *                  Casper Hornstrup
00030  *                  KJK::Hyperion
00031  *                  Guido de Jong
00032  *                  Ge van Geldorp
00033  *                  Royce Mitchell III
00034  *                  Filip Navara
00035  *                  Aleksey Bragin
00036  *                  Jason Filby
00037  *                  Thomas Weidenmueller
00038  *                  Gunnar Andre' Dalsnes
00039  *                  Mike Nordell
00040  *                  Alex Ionescu
00041  *                  Gregor Anich
00042  *                  Steven Edwards
00043  *                  Herve Poussineau
00044  */
00045 
00046 /*
00047 
00048 I've generally organized fault handling code in newmm as handlers that run
00049 under a single lock acquisition, check the state, and either take necessary
00050 action atomically, or place a wait entry and return a continuation to the
00051 caller.  This lends itself to code that has a simple, structured form,
00052 doesn't make assumptions about lock taking and breaking, and provides an
00053 obvious, graphic seperation between code that may block and code that isn't
00054 allowed to.  This file contains the non-blocking half.
00055 
00056 In order to request a blocking operation to happen outside locks, place a
00057 function pointer in the provided MM_REQUIRED_RESOURCES struct and return
00058 STATUS_MORE_PROCESSING_REQUIRED.  The function indicated will receive the
00059 provided struct and take action outside of any mm related locks and at
00060 PASSIVE_LEVEL.  The same fault handler will be called again after the
00061 blocking operation succeeds.  In this way, the fault handler can accumulate
00062 state, but will freely work while competing with other threads.
00063 
00064 Fault handlers in this file should check for an MM_WAIT_ENTRY in a page
00065 table they're using and return STATUS_SUCCESS + 1 if it's found.  In that
00066 case, the caller will wait on the wait entry event until the competing thread
00067 is finished, and recall this handler in the current thread.
00068 
00069 Another thing to note here is that we require mappings to exactly mirror
00070 rmaps, so each mapping should be immediately followed by an rmap addition.
00071 
00072 */
00073 
00074 /* INCLUDES *****************************************************************/
00075 
00076 #include <ntoskrnl.h>
00077 #include "newmm.h"
00078 #define NDEBUG
00079 #include <debug.h>
00080 #include "../mm/ARM3/miarm.h"
00081 
00082 #define DPRINTC DPRINT
00083 
00084 extern KEVENT MmWaitPageEvent;
00085 extern PMMWSL MmWorkingSetList;
00086 
00087 /*
00088 
00089 Multiple stage handling of a not-present fault in a data section.
00090 
00091 Required->State is used to accumulate flags that indicate the next action
00092 the handler should take.
00093 
00094 State & 2 is currently used to indicate that the page acquired by a previous
00095 callout is a global page to the section and should be placed in the section
00096 page table.
00097 
00098 Note that the primitive tail recursion done here reaches the base case when
00099 the page is present.
00100 
00101 */
00102 
00103 NTSTATUS
00104 NTAPI
00105 MmNotPresentFaultCachePage(PMMSUPPORT AddressSpace,
00106                            MEMORY_AREA* MemoryArea,
00107                            PVOID Address,
00108                            BOOLEAN Locked,
00109                            PMM_REQUIRED_RESOURCES Required)
00110 {
00111     NTSTATUS Status;
00112     PVOID PAddress;
00113     ULONG Consumer;
00114     PMM_SECTION_SEGMENT Segment;
00115     LARGE_INTEGER FileOffset, TotalOffset;
00116     ULONG_PTR Entry;
00117     ULONG Attributes;
00118     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
00119     KIRQL OldIrql;
00120 
00121     DPRINT("Not Present: %p %p (%p-%p)\n",
00122            AddressSpace,
00123            Address,
00124            MemoryArea->StartingAddress,
00125            MemoryArea->EndingAddress);
00126 
00127     /*
00128      * There is a window between taking the page fault and locking the
00129      * address space when another thread could load the page so we check
00130      * that.
00131      */
00132     if (MmIsPagePresent(Process, Address))
00133     {
00134         DPRINT("Done\n");
00135         return STATUS_SUCCESS;
00136     }
00137 
00138     PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
00139     TotalOffset.QuadPart = (ULONG_PTR)PAddress -
00140                            (ULONG_PTR)MemoryArea->StartingAddress;
00141 
00142     Segment = MemoryArea->Data.SectionData.Segment;
00143 
00144     TotalOffset.QuadPart += MemoryArea->Data.SectionData.ViewOffset.QuadPart;
00145     FileOffset = TotalOffset;
00146 
00147     //Consumer = (Segment->Flags & MM_DATAFILE_SEGMENT) ? MC_CACHE : MC_USER;
00148     Consumer = MC_CACHE;
00149 
00150     if (Segment->FileObject)
00151     {
00152         DPRINT("FileName %wZ\n", &Segment->FileObject->FileName);
00153     }
00154 
00155     DPRINT("Total Offset %08x%08x\n", TotalOffset.HighPart, TotalOffset.LowPart);
00156 
00157     /* Lock the segment */
00158     MmLockSectionSegment(Segment);
00159 
00160     /* Get the entry corresponding to the offset within the section */
00161     Entry = MmGetPageEntrySectionSegment(Segment, &TotalOffset);
00162 
00163     Attributes = PAGE_READONLY;
00164 
00165     if (Required->State && Required->Page[0])
00166     {
00167         DPRINT("Have file and page, set page %x in section @ %x #\n",
00168                Required->Page[0],
00169                TotalOffset.LowPart);
00170 
00171         if (Required->SwapEntry)
00172             MmSetSavedSwapEntryPage(Required->Page[0], Required->SwapEntry);
00173 
00174         if (Required->State & 2)
00175         {
00176             DPRINT("Set in section @ %x\n", TotalOffset.LowPart);
00177             Status = MmSetPageEntrySectionSegment(Segment,
00178                                                   &TotalOffset,
00179                                                   Entry = MAKE_PFN_SSE(Required->Page[0]));
00180             if (!NT_SUCCESS(Status))
00181             {
00182                 MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
00183             }
00184             MmUnlockSectionSegment(Segment);
00185             MiSetPageEvent(Process, Address);
00186             DPRINT("Status %x\n", Status);
00187             return STATUS_MM_RESTART_OPERATION;
00188         }
00189         else
00190         {
00191             DPRINT("Set %x in address space @ %x\n", Required->Page[0], Address);
00192             Status = MmCreateVirtualMapping(Process,
00193                                             Address,
00194                                             Attributes,
00195                                             Required->Page,
00196                                             1);
00197             if (NT_SUCCESS(Status))
00198             {
00199                 MmInsertRmap(Required->Page[0], Process, Address);
00200             }
00201             else
00202             {
00203                 /* Drop the reference for our address space ... */
00204                 MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
00205             }
00206             MmUnlockSectionSegment(Segment);
00207             DPRINTC("XXX Set Event %x\n", Status);
00208             MiSetPageEvent(Process, Address);
00209             DPRINT("Status %x\n", Status);
00210             return Status;
00211         }
00212     }
00213     else if (MM_IS_WAIT_PTE(Entry))
00214     {
00215         // Whenever MM_WAIT_ENTRY is required as a swap entry, we need to
00216         // ask the fault handler to wait until we should continue.  Rathern
00217         // than recopy this boilerplate code everywhere, we just ask them
00218         // to wait.
00219         MmUnlockSectionSegment(Segment);
00220         return STATUS_SUCCESS + 1;
00221     }
00222     else if (Entry)
00223     {
00224         PFN_NUMBER Page = PFN_FROM_SSE(Entry);
00225         DPRINT("Take reference to page %x #\n", Page);
00226 
00227         if (MiGetPfnEntry(Page) == NULL)
00228         {
00229             DPRINT1("Found no PFN entry for page 0x%x in page entry 0x%x (segment: 0x%p, offset: %08x%08x)\n",
00230                     Page,
00231                     Entry,
00232                     Segment,
00233                     TotalOffset.HighPart,
00234                     TotalOffset.LowPart);
00235             KeBugCheck(CACHE_MANAGER);
00236         }
00237 
00238         OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
00239         MmReferencePage(Page);
00240         KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
00241 
00242         Status = MmCreateVirtualMapping(Process, Address, Attributes, &Page, 1);
00243         if (NT_SUCCESS(Status))
00244         {
00245             MmInsertRmap(Page, Process, Address);
00246         }
00247         DPRINT("XXX Set Event %x\n", Status);
00248         MiSetPageEvent(Process, Address);
00249         MmUnlockSectionSegment(Segment);
00250         DPRINT("Status %x\n", Status);
00251         return Status;
00252     }
00253     else
00254     {
00255         DPRINT("Get page into section\n");
00256         /*
00257          * If the entry is zero (and it can't change because we have
00258          * locked the segment) then we need to load the page.
00259          */
00260         //DPRINT1("Read from file %08x %wZ\n", FileOffset.LowPart, &Section->FileObject->FileName);
00261         Required->State = 2;
00262         Required->Context = Segment->FileObject;
00263         Required->Consumer = Consumer;
00264         Required->FileOffset = FileOffset;
00265         Required->Amount = PAGE_SIZE;
00266         Required->DoAcquisition = MiReadFilePage;
00267 
00268         MmSetPageEntrySectionSegment(Segment,
00269                                      &TotalOffset,
00270                                      MAKE_SWAP_SSE(MM_WAIT_ENTRY));
00271 
00272         MmUnlockSectionSegment(Segment);
00273         return STATUS_MORE_PROCESSING_REQUIRED;
00274     }
00275     ASSERT(FALSE);
00276     return STATUS_ACCESS_VIOLATION;
00277 }
00278 
00279 NTSTATUS
00280 NTAPI
00281 MiCopyPageToPage(PFN_NUMBER DestPage, PFN_NUMBER SrcPage)
00282 {
00283     PEPROCESS Process;
00284     KIRQL Irql, Irql2;
00285     PVOID TempAddress, TempSource;
00286 
00287     Process = PsGetCurrentProcess();
00288     TempAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
00289     if (TempAddress == NULL)
00290     {
00291         return STATUS_NO_MEMORY;
00292     }
00293     TempSource = MiMapPageInHyperSpace(Process, SrcPage, &Irql2);
00294     if (!TempSource) {
00295         MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
00296         return STATUS_NO_MEMORY;
00297     }
00298 
00299     memcpy(TempAddress, TempSource, PAGE_SIZE);
00300 
00301     MiUnmapPageInHyperSpace(Process, TempSource, Irql2);
00302     MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
00303     return STATUS_SUCCESS;
00304 }
00305 
00306 /*
00307 
00308 This function is deceptively named, in that it does the actual work of handling
00309 access faults on data sections.  In the case of the code that's present here,
00310 we don't allow cow sections, but we do need this to unset the initial
00311 PAGE_READONLY condition of pages faulted into the cache so that we can add
00312 a dirty bit in the section page table on the first modification.
00313 
00314 In the ultimate form of this code, CoW is reenabled.
00315 
00316 */
00317 
00318 NTSTATUS
00319 NTAPI
00320 MiCowCacheSectionPage(PMMSUPPORT AddressSpace,
00321                       PMEMORY_AREA MemoryArea,
00322                       PVOID Address,
00323                       BOOLEAN Locked,
00324                       PMM_REQUIRED_RESOURCES Required)
00325 {
00326     PMM_SECTION_SEGMENT Segment;
00327     PFN_NUMBER NewPage, OldPage;
00328     NTSTATUS Status;
00329     PVOID PAddress;
00330     LARGE_INTEGER Offset;
00331     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
00332 
00333     DPRINT("MmAccessFaultSectionView(%x, %x, %x, %x)\n",
00334            AddressSpace,
00335            MemoryArea,
00336            Address,
00337            Locked);
00338 
00339     Segment = MemoryArea->Data.SectionData.Segment;
00340 
00341    /* Lock the segment */
00342     MmLockSectionSegment(Segment);
00343 
00344    /* Find the offset of the page */
00345     PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
00346     Offset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress +
00347                       MemoryArea->Data.SectionData.ViewOffset.QuadPart;
00348 
00349     if (!Segment->WriteCopy /*&&
00350         !MemoryArea->Data.SectionData.WriteCopyView*/ ||
00351         Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
00352     {
00353 #if 0
00354         if (Region->Protect == PAGE_READWRITE ||
00355             Region->Protect == PAGE_EXECUTE_READWRITE)
00356 #endif
00357         {
00358             ULONG_PTR Entry;
00359             DPRINTC("setting non-cow page %x %x:%x offset %x (%x) to writable\n",
00360                     Segment,
00361                     Process,
00362                     PAddress,
00363                     Offset.u.LowPart,
00364                     MmGetPfnForProcess(Process, Address));
00365             if (Segment->FileObject)
00366             {
00367                 DPRINTC("file %wZ\n", &Segment->FileObject->FileName);
00368             }
00369             Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
00370             DPRINT("Entry %x\n", Entry);
00371             if (Entry &&
00372                 !IS_SWAP_FROM_SSE(Entry) &&
00373                 PFN_FROM_SSE(Entry) == MmGetPfnForProcess(Process, Address)) {
00374 
00375                 MmSetPageEntrySectionSegment(Segment,
00376                                              &Offset,
00377                                              DIRTY_SSE(Entry));
00378             }
00379             MmSetPageProtect(Process, PAddress, PAGE_READWRITE);
00380             MmSetDirtyPage(Process, PAddress);
00381             MmUnlockSectionSegment(Segment);
00382             DPRINT("Done\n");
00383             return STATUS_SUCCESS;
00384         }
00385 #if 0
00386         else
00387         {
00388             DPRINT("Not supposed to be writable\n");
00389             MmUnlockSectionSegment(Segment);
00390             return STATUS_ACCESS_VIOLATION;
00391         }
00392 #endif
00393     }
00394 
00395     if (!Required->Page[0])
00396     {
00397         SWAPENTRY SwapEntry;
00398         if (MmIsPageSwapEntry(Process, Address))
00399         {
00400             MmGetPageFileMapping(Process, Address, &SwapEntry);
00401             MmUnlockSectionSegment(Segment);
00402             if (SwapEntry == MM_WAIT_ENTRY)
00403                 return STATUS_SUCCESS + 1; // Wait ... somebody else is getting it right now
00404             else
00405                 return STATUS_SUCCESS; // Nonwait swap entry ... handle elsewhere
00406         }
00407         /* Call out to acquire a page to copy to.  We'll be re-called when
00408          * the page has been allocated. */
00409         Required->Page[1] = MmGetPfnForProcess(Process, Address);
00410         Required->Consumer = MC_CACHE;
00411         Required->Amount = 1;
00412         Required->File = __FILE__;
00413         Required->Line = __LINE__;
00414         Required->DoAcquisition = MiGetOnePage;
00415         MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
00416         MmUnlockSectionSegment(Segment);
00417         return STATUS_MORE_PROCESSING_REQUIRED;
00418     }
00419 
00420     NewPage = Required->Page[0];
00421     OldPage = Required->Page[1];
00422 
00423     DPRINT("Allocated page %x\n", NewPage);
00424 
00425    /* Unshare the old page */
00426    MmDeleteRmap(OldPage, Process, PAddress);
00427 
00428    /* Copy the old page */
00429     DPRINT("Copying\n");
00430     MiCopyPageToPage(NewPage, OldPage);
00431 
00432    /* Set the PTE to point to the new page */
00433     Status = MmCreateVirtualMapping(Process,
00434                                     Address,
00435                                     PAGE_READWRITE,
00436                                     &NewPage,
00437                                     1);
00438 
00439     if (!NT_SUCCESS(Status))
00440     {
00441         DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
00442         ASSERT(FALSE);
00443         MmUnlockSectionSegment(Segment);
00444         return Status;
00445     }
00446 
00447     MmInsertRmap(NewPage, Process, PAddress);
00448     MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
00449     MmUnlockSectionSegment(Segment);
00450 
00451     DPRINT("Address 0x%.8X\n", Address);
00452     return STATUS_SUCCESS;
00453 }
00454 
00455 KEVENT MmWaitPageEvent;
00456 
00457 typedef struct _WORK_QUEUE_WITH_CONTEXT
00458 {
00459     WORK_QUEUE_ITEM WorkItem;
00460     PMMSUPPORT AddressSpace;
00461     PMEMORY_AREA MemoryArea;
00462     PMM_REQUIRED_RESOURCES Required;
00463     NTSTATUS Status;
00464     KEVENT Wait;
00465     AcquireResource DoAcquisition;
00466 } WORK_QUEUE_WITH_CONTEXT, *PWORK_QUEUE_WITH_CONTEXT;
00467 
00468 /*
00469 
00470 This is the work item used do blocking resource acquisition when a fault
00471 handler returns STATUS_MORE_PROCESSING_REQUIRED.  It's used to allow resource
00472 acquisition to take place on a different stack, and outside of any locks used
00473 by fault handling, making recursive fault handling possible when required.
00474 
00475 */
00476 
00477 VOID
00478 NTAPI
00479 MmpFaultWorker(PWORK_QUEUE_WITH_CONTEXT WorkItem)
00480 {
00481     DPRINT("Calling work\n");
00482     WorkItem->Status = WorkItem->Required->DoAcquisition(WorkItem->AddressSpace,
00483                                                          WorkItem->MemoryArea,
00484                                                          WorkItem->Required);
00485     DPRINT("Status %x\n", WorkItem->Status);
00486     KeSetEvent(&WorkItem->Wait, IO_NO_INCREMENT, FALSE);
00487 }
00488 
00489 /*
00490 
00491 This code seperates the action of fault handling into an upper and lower
00492 handler to allow the inner handler to optionally be called in work item
00493 if the stack is getting too deep.  My experiments show that the third
00494 recursive page fault taken at PASSIVE_LEVEL must be shunted away to a
00495 worker thread.  In the ultimate form of this code, the primary fault handler
00496 makes this decision by using a thread-local counter to detect a too-deep
00497 fault stack and call the inner fault handler in a worker thread if required.
00498 
00499 Note that faults are taken at passive level and have access to ordinary
00500 driver entry points such as those that read and write files, and filesystems
00501 should use paged structures whenever possible.  This makes recursive faults
00502 both a perfectly normal occurrance, and a worthwhile case to handle.
00503 
00504 The code below will repeatedly call MiCowSectionPage as long as it returns
00505 either STATUS_SUCCESS + 1 or STATUS_MORE_PROCESSING_REQUIRED.  In the more
00506 processing required case, we call out to a blocking resource acquisition
00507 function and then recall the faut handler with the shared state represented
00508 by the MM_REQUIRED_RESOURCES struct.
00509 
00510 In the other case, we wait on the wait entry event and recall the handler.
00511 Each time the wait entry event is signalled, one thread has removed an
00512 MM_WAIT_ENTRY from a page table.
00513 
00514 In the ultimate form of this code, there is a single system wide fault handler
00515 for each of access fault and not present and each memory area contains a
00516 function pointer that indicates the active fault handler.  Since the mm code
00517 in reactos is currently fragmented, I didn't bring this change to trunk.
00518 
00519 */
00520 
00521 NTSTATUS
00522 NTAPI
00523 MmpSectionAccessFaultInner(KPROCESSOR_MODE Mode,
00524                            PMMSUPPORT AddressSpace,
00525                            ULONG_PTR Address,
00526                            BOOLEAN FromMdl,
00527                            PETHREAD Thread)
00528 {
00529     MEMORY_AREA* MemoryArea;
00530     NTSTATUS Status;
00531     BOOLEAN Locked = FromMdl;
00532     MM_REQUIRED_RESOURCES Resources = { 0 };
00533     WORK_QUEUE_WITH_CONTEXT Context;
00534 
00535     RtlZeroMemory(&Context, sizeof(WORK_QUEUE_WITH_CONTEXT));
00536 
00537     DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
00538 
00539     if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
00540     {
00541         DPRINT1("Page fault at high IRQL was %d\n", KeGetCurrentIrql());
00542         return STATUS_UNSUCCESSFUL;
00543     }
00544 
00545     /* Find the memory area for the faulting address */
00546     if (Address >= (ULONG_PTR)MmSystemRangeStart)
00547     {
00548         /* Check permissions */
00549         if (Mode != KernelMode)
00550         {
00551             DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
00552             return STATUS_ACCESS_VIOLATION;
00553         }
00554         AddressSpace = MmGetKernelAddressSpace();
00555     }
00556     else
00557     {
00558         AddressSpace = &PsGetCurrentProcess()->Vm;
00559     }
00560 
00561     if (!FromMdl)
00562     {
00563         MmLockAddressSpace(AddressSpace);
00564     }
00565 
00566     do
00567     {
00568         MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
00569         if (MemoryArea == NULL ||
00570             MemoryArea->DeleteInProgress)
00571         {
00572             if (!FromMdl)
00573             {
00574                 MmUnlockAddressSpace(AddressSpace);
00575             }
00576             DPRINT("Address: %x\n", Address);
00577             return STATUS_ACCESS_VIOLATION;
00578         }
00579 
00580         DPRINT("Type %x (%x -> %x)\n",
00581                MemoryArea->Type,
00582                MemoryArea->StartingAddress,
00583                MemoryArea->EndingAddress);
00584 
00585         Resources.DoAcquisition = NULL;
00586 
00587         // Note: fault handlers are called with address space locked
00588         // We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
00589         Status = MiCowCacheSectionPage(AddressSpace,
00590                                        MemoryArea,
00591                                        (PVOID)Address,
00592                                        Locked,
00593                                        &Resources);
00594 
00595         if (!FromMdl)
00596         {
00597             MmUnlockAddressSpace(AddressSpace);
00598         }
00599 
00600         if (Status == STATUS_SUCCESS + 1)
00601         {
00602             /* Wait page ... */
00603             DPRINT("Waiting for %x\n", Address);
00604             MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
00605             DPRINT("Restarting fault %x\n", Address);
00606             Status = STATUS_MM_RESTART_OPERATION;
00607         }
00608         else if (Status == STATUS_MM_RESTART_OPERATION)
00609         {
00610             /* Clean slate */
00611             RtlZeroMemory(&Resources, sizeof(Resources));
00612         }
00613         else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
00614         {
00615             if (Thread->ActiveFaultCount > 0)
00616             {
00617                 DPRINT("Already fault handling ... going to work item (%x)\n",
00618                        Address);
00619                 Context.AddressSpace = AddressSpace;
00620                 Context.MemoryArea = MemoryArea;
00621                 Context.Required = &Resources;
00622                 KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
00623 
00624                 ExInitializeWorkItem(&Context.WorkItem,
00625                                      (PWORKER_THREAD_ROUTINE)MmpFaultWorker,
00626                                      &Context);
00627 
00628                 DPRINT("Queue work item\n");
00629                 ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
00630                 DPRINT("Wait\n");
00631                 KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
00632                 Status = Context.Status;
00633                 DPRINT("Status %x\n", Status);
00634             }
00635             else
00636             {
00637                 Status = Resources.DoAcquisition(AddressSpace, MemoryArea, &Resources);
00638             }
00639 
00640             if (NT_SUCCESS(Status))
00641             {
00642                 Status = STATUS_MM_RESTART_OPERATION;
00643             }
00644         }
00645 
00646         if (!FromMdl)
00647         {
00648             MmLockAddressSpace(AddressSpace);
00649         }
00650     }
00651     while (Status == STATUS_MM_RESTART_OPERATION);
00652 
00653     if (!NT_SUCCESS(Status) && MemoryArea->Type == 1)
00654     {
00655         DPRINT1("Completed page fault handling %x %x\n", Address, Status);
00656         DPRINT1("Type %x (%x -> %x)\n",
00657                 MemoryArea->Type,
00658                 MemoryArea->StartingAddress,
00659                 MemoryArea->EndingAddress);
00660     }
00661 
00662     if (!FromMdl)
00663     {
00664         MmUnlockAddressSpace(AddressSpace);
00665     }
00666 
00667     return Status;
00668 }
00669 
00670 /*
00671 
00672 This is the outer fault handler mentioned in the description of
00673 MmpSectionAccsesFaultInner.  It increments a fault depth count in the current
00674 thread.
00675 
00676 In the ultimate form of this code, the lower fault handler will optionally
00677 use the count to keep the kernel stack from overflowing.
00678 
00679 */
00680 
00681 NTSTATUS
00682 NTAPI
00683 MmAccessFaultCacheSection(KPROCESSOR_MODE Mode,
00684                           ULONG_PTR Address,
00685                           BOOLEAN FromMdl)
00686 {
00687     PETHREAD Thread;
00688     PMMSUPPORT AddressSpace;
00689     NTSTATUS Status;
00690 
00691     DPRINT("MmpAccessFault(Mode %d, Address %x)\n", Mode, Address);
00692 
00693     Thread = PsGetCurrentThread();
00694 
00695     if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
00696     {
00697         DPRINT1("Page fault at high IRQL %d, address %x\n",
00698                 KeGetCurrentIrql(),
00699                 Address);
00700         return STATUS_UNSUCCESSFUL;
00701     }
00702 
00703     /* Find the memory area for the faulting address */
00704     if (Address >= (ULONG_PTR)MmSystemRangeStart)
00705     {
00706         /* Check permissions */
00707         if (Mode != KernelMode)
00708         {
00709             DPRINT1("Address: %x:%x\n", PsGetCurrentProcess(), Address);
00710             return STATUS_ACCESS_VIOLATION;
00711         }
00712         AddressSpace = MmGetKernelAddressSpace();
00713     }
00714     else
00715     {
00716         AddressSpace = &PsGetCurrentProcess()->Vm;
00717     }
00718 
00719     Thread->ActiveFaultCount++;
00720     Status = MmpSectionAccessFaultInner(Mode,
00721                                         AddressSpace,
00722                                         Address,
00723                                         FromMdl,
00724                                         Thread);
00725     Thread->ActiveFaultCount--;
00726 
00727     return Status;
00728 }
00729 
00730 /*
00731 
00732 As above, this code seperates the active part of fault handling from a carrier
00733 that can use the thread's active fault count to determine whether a work item
00734 is required.  Also as above, this function repeatedly calls the active not
00735 present fault handler until a clear success or failure is received, using a
00736 return of STATUS_MORE_PROCESSING_REQUIRED or STATUS_SUCCESS + 1.
00737 
00738 */
00739 
00740 NTSTATUS
00741 NTAPI
00742 MmNotPresentFaultCacheSectionInner(KPROCESSOR_MODE Mode,
00743                                    PMMSUPPORT AddressSpace,
00744                                    ULONG_PTR Address,
00745                                    BOOLEAN FromMdl,
00746                                    PETHREAD Thread)
00747 {
00748     BOOLEAN Locked = FromMdl;
00749     PMEMORY_AREA MemoryArea;
00750     MM_REQUIRED_RESOURCES Resources = { 0 };
00751     WORK_QUEUE_WITH_CONTEXT Context;
00752     NTSTATUS Status = STATUS_SUCCESS;
00753 
00754     RtlZeroMemory(&Context, sizeof(WORK_QUEUE_WITH_CONTEXT));
00755 
00756     if (!FromMdl)
00757     {
00758         MmLockAddressSpace(AddressSpace);
00759     }
00760 
00761     /* Call the memory area specific fault handler */
00762     do
00763     {
00764         MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
00765         if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
00766         {
00767             Status = STATUS_ACCESS_VIOLATION;
00768             if (MemoryArea)
00769             {
00770                 DPRINT1("Type %x DIP %x\n",
00771                         MemoryArea->Type,
00772                         MemoryArea->DeleteInProgress);
00773             }
00774             else
00775             {
00776                 DPRINT1("No memory area\n");
00777             }
00778             DPRINT1("Process %x, Address %x\n",
00779                     MmGetAddressSpaceOwner(AddressSpace),
00780                     Address);
00781             break;
00782         }
00783 
00784         DPRINTC("Type %x (%x -> %x -> %x) in %x\n",
00785                 MemoryArea->Type,
00786                 MemoryArea->StartingAddress,
00787                 Address,
00788                 MemoryArea->EndingAddress,
00789                 PsGetCurrentThread());
00790 
00791         Resources.DoAcquisition = NULL;
00792 
00793         // Note: fault handlers are called with address space locked
00794         // We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
00795 
00796         Status = MmNotPresentFaultCachePage(AddressSpace,
00797                                             MemoryArea,
00798                                             (PVOID)Address,
00799                                             Locked,
00800                                             &Resources);
00801 
00802         if (!FromMdl)
00803         {
00804             MmUnlockAddressSpace(AddressSpace);
00805         }
00806 
00807         if (Status == STATUS_SUCCESS)
00808         {
00809             ; // Nothing
00810         }
00811         else if (Status == STATUS_SUCCESS + 1)
00812         {
00813             /* Wait page ... */
00814             DPRINT("Waiting for %x\n", Address);
00815             MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
00816             DPRINT("Done waiting for %x\n", Address);
00817             Status = STATUS_MM_RESTART_OPERATION;
00818         }
00819         else if (Status == STATUS_MM_RESTART_OPERATION)
00820         {
00821             /* Clean slate */
00822             DPRINT("Clear resource\n");
00823             RtlZeroMemory(&Resources, sizeof(Resources));
00824         }
00825         else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
00826         {
00827             if (Thread->ActiveFaultCount > 2)
00828             {
00829                 DPRINTC("Already fault handling ... going to work item (%x)\n", Address);
00830                 Context.AddressSpace = AddressSpace;
00831                 Context.MemoryArea = MemoryArea;
00832                 Context.Required = &Resources;
00833                 KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
00834 
00835                 ExInitializeWorkItem(&Context.WorkItem,
00836                                      (PWORKER_THREAD_ROUTINE)MmpFaultWorker,
00837                                      &Context);
00838 
00839                 DPRINT("Queue work item\n");
00840                 ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
00841                 DPRINT("Wait\n");
00842                 KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
00843                 Status = Context.Status;
00844                 DPRINTC("Status %x\n", Status);
00845             }
00846             else
00847             {
00848                 DPRINT("DoAcquisition %x\n", Resources.DoAcquisition);
00849 
00850                 Status = Resources.DoAcquisition(AddressSpace,
00851                                                  MemoryArea,
00852                                                  &Resources);
00853 
00854                 DPRINT("DoAcquisition %x -> %x\n",
00855                        Resources.DoAcquisition,
00856                        Status);
00857             }
00858 
00859             if (NT_SUCCESS(Status))
00860             {
00861                 Status = STATUS_MM_RESTART_OPERATION;
00862             }
00863         }
00864         else if (NT_SUCCESS(Status))
00865         {
00866             ASSERT(FALSE);
00867         }
00868 
00869         if (!FromMdl)
00870         {
00871             MmLockAddressSpace(AddressSpace);
00872         }
00873     }
00874     while (Status == STATUS_MM_RESTART_OPERATION);
00875 
00876     DPRINTC("Completed page fault handling: %x:%x %x\n",
00877             MmGetAddressSpaceOwner(AddressSpace),
00878             Address,
00879             Status);
00880 
00881     if (!FromMdl)
00882     {
00883         MmUnlockAddressSpace(AddressSpace);
00884     }
00885 
00886     MiSetPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
00887     DPRINT("Done %x\n", Status);
00888 
00889     return Status;
00890 }
00891 
00892 /*
00893 
00894 Call the inner not present fault handler, keeping track of the fault count.
00895 In the ultimate form of this code, optionally use a worker thread the handle
00896 the fault in order to sidestep stack overflow in the multiple fault case.
00897 
00898 */
00899 
00900 NTSTATUS
00901 NTAPI
00902 MmNotPresentFaultCacheSection(KPROCESSOR_MODE Mode,
00903                               ULONG_PTR Address,
00904                               BOOLEAN FromMdl)
00905 {
00906     PETHREAD Thread;
00907     PMMSUPPORT AddressSpace;
00908     NTSTATUS Status;
00909 
00910     Address &= ~(PAGE_SIZE - 1);
00911     DPRINT("MmNotPresentFault(Mode %d, Address %x)\n", Mode, Address);
00912 
00913     Thread = PsGetCurrentThread();
00914 
00915     if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
00916     {
00917         DPRINT1("Page fault at high IRQL %d, address %x\n",
00918                 KeGetCurrentIrql(),
00919                 Address);
00920 
00921         ASSERT(FALSE);
00922         return STATUS_UNSUCCESSFUL;
00923     }
00924 
00925     /* Find the memory area for the faulting address */
00926     if (Address >= (ULONG_PTR)MmSystemRangeStart)
00927     {
00928         /* Check permissions */
00929         if (Mode != KernelMode)
00930         {
00931             DPRINTC("Address: %x\n", Address);
00932             return STATUS_ACCESS_VIOLATION;
00933         }
00934         AddressSpace = MmGetKernelAddressSpace();
00935     }
00936     else
00937     {
00938         AddressSpace = &PsGetCurrentProcess()->Vm;
00939     }
00940 
00941     Thread->ActiveFaultCount++;
00942     Status = MmNotPresentFaultCacheSectionInner(Mode,
00943                                                 AddressSpace,
00944                                                 Address,
00945                                                 FromMdl,
00946                                                 Thread);
00947     Thread->ActiveFaultCount--;
00948 
00949     ASSERT(Status != STATUS_UNSUCCESSFUL);
00950     ASSERT(Status != STATUS_INVALID_PARAMETER);
00951     DPRINT("MmAccessFault %x:%x -> %x\n",
00952            MmGetAddressSpaceOwner(AddressSpace),
00953            Address,
00954            Status);
00955 
00956     return Status;
00957 }

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