Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenfault.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
1.7.6.1
|