[NTOSKRNL/NEWCC]
[reactos.git] / reactos / ntoskrnl / cache / section / fault.c
1 /*
2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/section/fault.c
21 * PURPOSE: Consolidate fault handlers for sections
22 *
23 * PROGRAMMERS: Arty
24 * Rex Jolliff
25 * David Welch
26 * Eric Kohl
27 * Emanuele Aliberti
28 * Eugene Ingerman
29 * Casper Hornstrup
30 * KJK::Hyperion
31 * Guido de Jong
32 * Ge van Geldorp
33 * Royce Mitchell III
34 * Filip Navara
35 * Aleksey Bragin
36 * Jason Filby
37 * Thomas Weidenmueller
38 * Gunnar Andre' Dalsnes
39 * Mike Nordell
40 * Alex Ionescu
41 * Gregor Anich
42 * Steven Edwards
43 * Herve Poussineau
44 */
45
46 /* INCLUDES *****************************************************************/
47
48 #include <ntoskrnl.h>
49 #include "newmm.h"
50 #define NDEBUG
51 #include <debug.h>
52
53 #define DPRINTC DPRINT
54
55 extern KEVENT MmWaitPageEvent;
56
57 NTSTATUS
58 NTAPI
59 MmNotPresentFaultCachePage
60 (PMMSUPPORT AddressSpace,
61 MEMORY_AREA* MemoryArea,
62 PVOID Address,
63 BOOLEAN Locked,
64 PMM_REQUIRED_RESOURCES Required)
65 {
66 NTSTATUS Status;
67 PVOID PAddress;
68 ULONG Consumer;
69 PMM_CACHE_SECTION_SEGMENT Segment;
70 LARGE_INTEGER FileOffset, TotalOffset;
71 ULONG Entry;
72 ULONG Attributes;
73 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
74
75 DPRINT("Not Present: %p %p (%p-%p)\n", AddressSpace, Address, MemoryArea->StartingAddress, MemoryArea->EndingAddress);
76
77 /*
78 * There is a window between taking the page fault and locking the
79 * address space when another thread could load the page so we check
80 * that.
81 */
82 if (MmIsPagePresent(Process, Address))
83 {
84 DPRINT("Done\n");
85 return(STATUS_SUCCESS);
86 }
87
88 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
89 TotalOffset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress;
90
91 Segment = MemoryArea->Data.CacheData.Segment;
92
93 TotalOffset.QuadPart += MemoryArea->Data.CacheData.ViewOffset.QuadPart;
94 FileOffset = TotalOffset;
95
96 //Consumer = (Segment->Flags & MM_DATAFILE_SEGMENT) ? MC_CACHE : MC_USER;
97 Consumer = MC_CACHE;
98
99 if (Segment->FileObject)
100 {
101 DPRINT("FileName %wZ\n", &Segment->FileObject->FileName);
102 }
103
104 DPRINT("Total Offset %08x%08x\n", TotalOffset.HighPart, TotalOffset.LowPart);
105
106 /*
107 * Lock the segment
108 */
109 MmLockCacheSectionSegment(Segment);
110
111 /*
112 * Get the entry corresponding to the offset within the section
113 */
114 Entry = MiGetPageEntryCacheSectionSegment(Segment, &TotalOffset);
115
116 Attributes = PAGE_READONLY;
117
118 if (Required->State && Required->Page[0])
119 {
120 DPRINT("Have file and page, set page %x in section @ %x #\n", Required->Page[0], TotalOffset.LowPart);
121
122 if (Required->SwapEntry)
123 MmSetSavedSwapEntryPage(Required->Page[0], Required->SwapEntry);
124
125 if (Required->State & 2)
126 {
127 DPRINT("Set in section @ %x\n", TotalOffset.LowPart);
128 Status = MiSetPageEntryCacheSectionSegment
129 (Segment, &TotalOffset, Entry = MAKE_PFN_SSE(Required->Page[0]));
130 if (!NT_SUCCESS(Status))
131 {
132 MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
133 }
134 MmUnlockCacheSectionSegment(Segment);
135 MiSetPageEvent(Process, Address);
136 DPRINT("Status %x\n", Status);
137 return STATUS_MM_RESTART_OPERATION;
138 }
139 else
140 {
141 DPRINT("Set %x in address space @ %x\n", Required->Page[0], Address);
142 Status = MmCreateVirtualMapping(Process, Address, Attributes, Required->Page, 1);
143 if (NT_SUCCESS(Status))
144 {
145 MmInsertRmap(Required->Page[0], Process, Address);
146 }
147 else
148 {
149 // Drop the reference for our address space ...
150 MmReleasePageMemoryConsumer(MC_CACHE, Required->Page[0]);
151 }
152 MmUnlockCacheSectionSegment(Segment);
153 DPRINTC("XXX Set Event %x\n", Status);
154 MiSetPageEvent(Process, Address);
155 DPRINT("Status %x\n", Status);
156 return Status;
157 }
158 }
159 else if (Entry)
160 {
161 PFN_NUMBER Page = PFN_FROM_SSE(Entry);
162 DPRINT("Take reference to page %x #\n", Page);
163
164 MmReferencePage(Page);
165
166 Status = MmCreateVirtualMapping(Process, Address, Attributes, &Page, 1);
167 if (NT_SUCCESS(Status))
168 {
169 MmInsertRmap(Page, Process, Address);
170 }
171 DPRINT("XXX Set Event %x\n", Status);
172 MiSetPageEvent(Process, Address);
173 MmUnlockCacheSectionSegment(Segment);
174 DPRINT("Status %x\n", Status);
175 return Status;
176 }
177 else
178 {
179 DPRINT("Get page into section\n");
180 /*
181 * If the entry is zero (and it can't change because we have
182 * locked the segment) then we need to load the page.
183 */
184 //DPRINT1("Read from file %08x %wZ\n", FileOffset.LowPart, &Section->FileObject->FileName);
185 Required->State = 2;
186 Required->Context = Segment->FileObject;
187 Required->Consumer = Consumer;
188 Required->FileOffset = FileOffset;
189 Required->Amount = PAGE_SIZE;
190 Required->DoAcquisition = MiReadFilePage;
191 MiSetPageEntryCacheSectionSegment(Segment, &TotalOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
192 MmUnlockCacheSectionSegment(Segment);
193 return STATUS_MORE_PROCESSING_REQUIRED;
194 }
195 ASSERT(FALSE);
196 return STATUS_ACCESS_VIOLATION;
197 }
198
199 NTSTATUS
200 NTAPI
201 MiCopyPageToPage(PFN_NUMBER DestPage, PFN_NUMBER SrcPage)
202 {
203 PEPROCESS Process;
204 KIRQL Irql, Irql2;
205 PVOID TempAddress, TempSource;
206
207 Process = PsGetCurrentProcess();
208 TempAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
209 if (TempAddress == NULL)
210 {
211 return(STATUS_NO_MEMORY);
212 }
213 TempSource = MiMapPageInHyperSpace(Process, SrcPage, &Irql2);
214 if (!TempSource) {
215 MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
216 return(STATUS_NO_MEMORY);
217 }
218
219 memcpy(TempAddress, TempSource, PAGE_SIZE);
220
221 MiUnmapPageInHyperSpace(Process, TempSource, Irql2);
222 MiUnmapPageInHyperSpace(Process, TempAddress, Irql);
223 return(STATUS_SUCCESS);
224 }
225
226 NTSTATUS
227 NTAPI
228 MiCowCacheSectionPage
229 (PMMSUPPORT AddressSpace,
230 PMEMORY_AREA MemoryArea,
231 PVOID Address,
232 BOOLEAN Locked,
233 PMM_REQUIRED_RESOURCES Required)
234 {
235 PMM_CACHE_SECTION_SEGMENT Segment;
236 PFN_NUMBER NewPage, OldPage;
237 NTSTATUS Status;
238 PVOID PAddress;
239 LARGE_INTEGER Offset;
240 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
241
242 DPRINT("MmAccessFaultSectionView(%x, %x, %x, %x)\n", AddressSpace, MemoryArea, Address, Locked);
243
244 Segment = MemoryArea->Data.CacheData.Segment;
245
246 /*
247 * Lock the segment
248 */
249 MmLockCacheSectionSegment(Segment);
250
251 /*
252 * Find the offset of the page
253 */
254 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
255 Offset.QuadPart = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress +
256 MemoryArea->Data.CacheData.ViewOffset.QuadPart;
257
258 #if 0 // XXX Cache sections are not CoW. For now, treat all access violations this way.
259 if ((!Segment->WriteCopy &&
260 !MemoryArea->Data.CacheData.WriteCopyView) ||
261 Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
262 #endif
263 {
264 #if 0 // XXX Cache sections don't have regions at present, which streamlines things
265 if (Region->Protect == PAGE_READWRITE ||
266 Region->Protect == PAGE_EXECUTE_READWRITE)
267 #endif
268 {
269 ULONG Entry;
270 DPRINTC("setting non-cow page %x %x:%x offset %x (%x) to writable\n", Segment, Process, PAddress, Offset.u.LowPart, MmGetPfnForProcess(Process, Address));
271 if (Segment->FileObject)
272 {
273 DPRINTC("file %wZ\n", &Segment->FileObject->FileName);
274 }
275 Entry = MiGetPageEntryCacheSectionSegment(Segment, &Offset);
276 DPRINT("Entry %x\n", Entry);
277 if (Entry &&
278 !IS_SWAP_FROM_SSE(Entry) &&
279 PFN_FROM_SSE(Entry) == MmGetPfnForProcess(Process, Address)) {
280 MiSetPageEntryCacheSectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
281 }
282 MmSetPageProtect(Process, PAddress, PAGE_READWRITE);
283 MmUnlockCacheSectionSegment(Segment);
284 DPRINT("Done\n");
285 return STATUS_SUCCESS;
286 }
287 #if 0
288 else
289 {
290 DPRINT("Not supposed to be writable\n");
291 MmUnlockCacheSectionSegment(Segment);
292 return STATUS_ACCESS_VIOLATION;
293 }
294 #endif
295 }
296
297 if (!Required->Page[0])
298 {
299 SWAPENTRY SwapEntry;
300 if (MmIsPageSwapEntry(Process, Address))
301 {
302 MmGetPageFileMapping(Process, Address, &SwapEntry);
303 MmUnlockCacheSectionSegment(Segment);
304 if (SwapEntry == MM_WAIT_ENTRY)
305 return STATUS_SUCCESS + 1; // Wait ... somebody else is getting it right now
306 else
307 return STATUS_SUCCESS; // Nonwait swap entry ... handle elsewhere
308 }
309 Required->Page[1] = MmGetPfnForProcess(Process, Address);
310 Required->Consumer = MC_CACHE;
311 Required->Amount = 1;
312 Required->File = __FILE__;
313 Required->Line = __LINE__;
314 Required->DoAcquisition = MiGetOnePage;
315 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
316 MmUnlockCacheSectionSegment(Segment);
317 return STATUS_MORE_PROCESSING_REQUIRED;
318 }
319
320 NewPage = Required->Page[0];
321 OldPage = Required->Page[1];
322
323 DPRINT("Allocated page %x\n", NewPage);
324
325 /*
326 * Unshare the old page.
327 */
328 MmDeleteRmap(OldPage, Process, PAddress);
329
330 /*
331 * Copy the old page
332 */
333 DPRINT("Copying\n");
334 MiCopyPageToPage(NewPage, OldPage);
335
336 /*
337 * Set the PTE to point to the new page
338 */
339 Status = MmCreateVirtualMapping
340 (Process, Address, PAGE_READWRITE, &NewPage, 1);
341
342 if (!NT_SUCCESS(Status))
343 {
344 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
345 ASSERT(FALSE);
346 MmUnlockCacheSectionSegment(Segment);
347 return(Status);
348 }
349
350 MmInsertRmap(NewPage, Process, PAddress);
351 MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
352 MmUnlockCacheSectionSegment(Segment);
353
354 DPRINT("Address 0x%.8X\n", Address);
355 return(STATUS_SUCCESS);
356 }
357
358 KEVENT MmWaitPageEvent;
359
360 typedef struct _WORK_QUEUE_WITH_CONTEXT
361 {
362 WORK_QUEUE_ITEM WorkItem;
363 PMMSUPPORT AddressSpace;
364 PMEMORY_AREA MemoryArea;
365 PMM_REQUIRED_RESOURCES Required;
366 NTSTATUS Status;
367 KEVENT Wait;
368 AcquireResource DoAcquisition;
369 } WORK_QUEUE_WITH_CONTEXT, *PWORK_QUEUE_WITH_CONTEXT;
370
371 VOID
372 NTAPI
373 MmpFaultWorker
374 (PWORK_QUEUE_WITH_CONTEXT WorkItem)
375 {
376 DPRINT("Calling work\n");
377 WorkItem->Status =
378 WorkItem->Required->DoAcquisition
379 (WorkItem->AddressSpace,
380 WorkItem->MemoryArea,
381 WorkItem->Required);
382 DPRINT("Status %x\n", WorkItem->Status);
383 KeSetEvent(&WorkItem->Wait, IO_NO_INCREMENT, FALSE);
384 }
385
386 NTSTATUS
387 NTAPI
388 MmpSectionAccessFaultInner
389 (KPROCESSOR_MODE Mode,
390 PMMSUPPORT AddressSpace,
391 ULONG_PTR Address,
392 BOOLEAN FromMdl,
393 PETHREAD Thread)
394 {
395 MEMORY_AREA* MemoryArea;
396 NTSTATUS Status;
397 BOOLEAN Locked = FromMdl;
398 MM_REQUIRED_RESOURCES Resources = { 0 };
399
400 DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
401
402 if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
403 {
404 DPRINT1("Page fault at high IRQL was %d\n", KeGetCurrentIrql());
405 return(STATUS_UNSUCCESSFUL);
406 }
407
408 /*
409 * Find the memory area for the faulting address
410 */
411 if (Address >= (ULONG_PTR)MmSystemRangeStart)
412 {
413 /*
414 * Check permissions
415 */
416 if (Mode != KernelMode)
417 {
418 DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
419 return(STATUS_ACCESS_VIOLATION);
420 }
421 AddressSpace = MmGetKernelAddressSpace();
422 }
423 else
424 {
425 AddressSpace = &PsGetCurrentProcess()->Vm;
426 }
427
428 if (!FromMdl)
429 {
430 MmLockAddressSpace(AddressSpace);
431 }
432
433 do
434 {
435 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
436 if (MemoryArea == NULL ||
437 MemoryArea->DeleteInProgress)
438 {
439 if (!FromMdl)
440 {
441 MmUnlockAddressSpace(AddressSpace);
442 }
443 DPRINT("Address: %x\n", Address);
444 return (STATUS_ACCESS_VIOLATION);
445 }
446
447 DPRINT
448 ("Type %x (%x -> %x)\n",
449 MemoryArea->Type,
450 MemoryArea->StartingAddress,
451 MemoryArea->EndingAddress);
452
453 Resources.DoAcquisition = NULL;
454
455 // Note: fault handlers are called with address space locked
456 // We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
457 Status = MiCowCacheSectionPage
458 (AddressSpace, MemoryArea, (PVOID)Address, Locked, &Resources);
459
460 if (!FromMdl)
461 {
462 MmUnlockAddressSpace(AddressSpace);
463 }
464
465 if (Status == STATUS_SUCCESS + 1)
466 {
467 // Wait page ...
468 DPRINT("Waiting for %x\n", Address);
469 MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
470 DPRINT("Restarting fault %x\n", Address);
471 Status = STATUS_MM_RESTART_OPERATION;
472 }
473 else if (Status == STATUS_MM_RESTART_OPERATION)
474 {
475 // Clean slate
476 RtlZeroMemory(&Resources, sizeof(Resources));
477 }
478 else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
479 {
480 if (Thread->ActiveFaultCount > 0)
481 {
482 WORK_QUEUE_WITH_CONTEXT Context = {0};
483 DPRINT("Already fault handling ... going to work item (%x)\n", Address);
484 Context.AddressSpace = AddressSpace;
485 Context.MemoryArea = MemoryArea;
486 Context.Required = &Resources;
487 KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
488 ExInitializeWorkItem(&Context.WorkItem, (PWORKER_THREAD_ROUTINE)MmpFaultWorker, &Context);
489 DPRINT("Queue work item\n");
490 ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
491 DPRINT("Wait\n");
492 KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
493 Status = Context.Status;
494 DPRINT("Status %x\n", Status);
495 }
496 else
497 {
498 Status = Resources.DoAcquisition(AddressSpace, MemoryArea, &Resources);
499 }
500
501 if (NT_SUCCESS(Status))
502 {
503 Status = STATUS_MM_RESTART_OPERATION;
504 }
505 }
506
507 if (!FromMdl)
508 {
509 MmLockAddressSpace(AddressSpace);
510 }
511 }
512 while (Status == STATUS_MM_RESTART_OPERATION);
513
514 if (!NT_SUCCESS(Status) && MemoryArea->Type == 1)
515 {
516 DPRINT1("Completed page fault handling %x %x\n", Address, Status);
517 DPRINT1
518 ("Type %x (%x -> %x)\n",
519 MemoryArea->Type,
520 MemoryArea->StartingAddress,
521 MemoryArea->EndingAddress);
522 }
523
524 if (!FromMdl)
525 {
526 MmUnlockAddressSpace(AddressSpace);
527 }
528
529 return(Status);
530 }
531
532 NTSTATUS
533 NTAPI
534 MmAccessFaultCacheSection
535 (KPROCESSOR_MODE Mode,
536 ULONG_PTR Address,
537 BOOLEAN FromMdl)
538 {
539 PETHREAD Thread;
540 PMMSUPPORT AddressSpace;
541 NTSTATUS Status;
542
543 DPRINT("MmpAccessFault(Mode %d, Address %x)\n", Mode, Address);
544
545 Thread = PsGetCurrentThread();
546
547 if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
548 {
549 DPRINT1("Page fault at high IRQL %d, address %x\n", KeGetCurrentIrql(), Address);
550 return(STATUS_UNSUCCESSFUL);
551 }
552
553 /*
554 * Find the memory area for the faulting address
555 */
556 if (Address >= (ULONG_PTR)MmSystemRangeStart)
557 {
558 /*
559 * Check permissions
560 */
561 if (Mode != KernelMode)
562 {
563 DPRINT1("Address: %x:%x\n", PsGetCurrentProcess(), Address);
564 return(STATUS_ACCESS_VIOLATION);
565 }
566 AddressSpace = MmGetKernelAddressSpace();
567 }
568 else
569 {
570 AddressSpace = &PsGetCurrentProcess()->Vm;
571 }
572
573 Thread->ActiveFaultCount++;
574 Status = MmpSectionAccessFaultInner(Mode, AddressSpace, Address, FromMdl, Thread);
575 Thread->ActiveFaultCount--;
576
577 return(Status);
578 }
579
580 NTSTATUS
581 NTAPI
582 MmNotPresentFaultCacheSectionInner
583 (KPROCESSOR_MODE Mode,
584 PMMSUPPORT AddressSpace,
585 ULONG_PTR Address,
586 BOOLEAN FromMdl,
587 PETHREAD Thread)
588 {
589 BOOLEAN Locked = FromMdl;
590 PMEMORY_AREA MemoryArea;
591 MM_REQUIRED_RESOURCES Resources = { 0 };
592 NTSTATUS Status = STATUS_SUCCESS;
593
594 if (!FromMdl)
595 {
596 MmLockAddressSpace(AddressSpace);
597 }
598
599 /*
600 * Call the memory area specific fault handler
601 */
602 do
603 {
604 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
605 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
606 {
607 Status = STATUS_ACCESS_VIOLATION;
608 if (MemoryArea)
609 {
610 DPRINT1("Type %x DIP %x\n", MemoryArea->Type, MemoryArea->DeleteInProgress);
611 }
612 else
613 {
614 DPRINT1("No memory area\n");
615 }
616 DPRINT1("Process %x, Address %x\n", MmGetAddressSpaceOwner(AddressSpace), Address);
617 break;
618 }
619
620 DPRINTC
621 ("Type %x (%x -> %x -> %x) in %x\n",
622 MemoryArea->Type,
623 MemoryArea->StartingAddress,
624 Address,
625 MemoryArea->EndingAddress,
626 PsGetCurrentThread());
627
628 Resources.DoAcquisition = NULL;
629
630 // Note: fault handlers are called with address space locked
631 // We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
632
633 Status = MmNotPresentFaultCachePage
634 (AddressSpace, MemoryArea, (PVOID)Address, Locked, &Resources);
635
636 if (!FromMdl)
637 {
638 MmUnlockAddressSpace(AddressSpace);
639 }
640
641 if (Status == STATUS_SUCCESS)
642 {
643 ; // Nothing
644 }
645 else if (Status == STATUS_SUCCESS + 1)
646 {
647 // Wait page ...
648 DPRINT("Waiting for %x\n", Address);
649 MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
650 DPRINT("Done waiting for %x\n", Address);
651 Status = STATUS_MM_RESTART_OPERATION;
652 }
653 else if (Status == STATUS_MM_RESTART_OPERATION)
654 {
655 // Clean slate
656 DPRINT("Clear resource\n");
657 RtlZeroMemory(&Resources, sizeof(Resources));
658 }
659 else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
660 {
661 if (Thread->ActiveFaultCount > 1)
662 {
663 WORK_QUEUE_WITH_CONTEXT Context = {0};
664 DPRINTC("Already fault handling ... going to work item (%x)\n", Address);
665 Context.AddressSpace = AddressSpace;
666 Context.MemoryArea = MemoryArea;
667 Context.Required = &Resources;
668 KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
669 ExInitializeWorkItem(&Context.WorkItem, (PWORKER_THREAD_ROUTINE)MmpFaultWorker, &Context);
670 DPRINT("Queue work item\n");
671 ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
672 DPRINT("Wait\n");
673 KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
674 Status = Context.Status;
675 DPRINTC("Status %x\n", Status);
676 }
677 else
678 {
679 DPRINT("DoAcquisition %x\n", Resources.DoAcquisition);
680 Status = Resources.DoAcquisition
681 (AddressSpace, MemoryArea, &Resources);
682 DPRINT("DoAcquisition %x -> %x\n", Resources.DoAcquisition, Status);
683 }
684
685 if (NT_SUCCESS(Status))
686 {
687 Status = STATUS_MM_RESTART_OPERATION;
688 }
689 }
690 else if (NT_SUCCESS(Status))
691 {
692 ASSERT(FALSE);
693 }
694
695 if (!FromMdl)
696 {
697 MmLockAddressSpace(AddressSpace);
698 }
699 }
700 while (Status == STATUS_MM_RESTART_OPERATION);
701
702 DPRINTC("Completed page fault handling: %x:%x %x\n", MmGetAddressSpaceOwner(AddressSpace), Address, Status);
703 if (!FromMdl)
704 {
705 MmUnlockAddressSpace(AddressSpace);
706 }
707
708 MiSetPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
709 DPRINT("Done %x\n", Status);
710
711 return Status;
712 }
713
714 NTSTATUS
715 NTAPI
716 MmNotPresentFaultCacheSection
717 (KPROCESSOR_MODE Mode,
718 ULONG_PTR Address,
719 BOOLEAN FromMdl)
720 {
721 PETHREAD Thread;
722 PMMSUPPORT AddressSpace;
723 NTSTATUS Status;
724
725 Address &= ~(PAGE_SIZE - 1);
726 DPRINT("MmNotPresentFault(Mode %d, Address %x)\n", Mode, Address);
727
728 Thread = PsGetCurrentThread();
729
730 if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
731 {
732 DPRINT1("Page fault at high IRQL %d, address %x\n", KeGetCurrentIrql(), Address);
733 ASSERT(FALSE);
734 return(STATUS_UNSUCCESSFUL);
735 }
736
737 /*
738 * Find the memory area for the faulting address
739 */
740 if (Address >= (ULONG_PTR)MmSystemRangeStart)
741 {
742 /*
743 * Check permissions
744 */
745 if (Mode != KernelMode)
746 {
747 DPRINTC("Address: %x\n", Address);
748 return(STATUS_ACCESS_VIOLATION);
749 }
750 AddressSpace = MmGetKernelAddressSpace();
751 }
752 else
753 {
754 AddressSpace = &PsGetCurrentProcess()->Vm;
755 }
756
757 Thread->ActiveFaultCount++;
758 Status = MmNotPresentFaultCacheSectionInner
759 (Mode, AddressSpace, Address, FromMdl, Thread);
760 Thread->ActiveFaultCount--;
761
762 ASSERT(Status != STATUS_UNSUCCESSFUL);
763 ASSERT(Status != STATUS_INVALID_PARAMETER);
764 DPRINT("MmAccessFault %x:%x -> %x\n", MmGetAddressSpaceOwner(AddressSpace), Address, Status);
765
766 return(Status);
767 }