204c264b579b9d679e4084855aca3b07c9eebd42
[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 DPRINTC("setting non-cow page %x %x:%x offset %x (%x) to writable\n", Segment, Process, PAddress, Offset.u.LowPart, MmGetPfnForProcess(Process, Address));
270 if (Segment->FileObject)
271 {
272 DPRINTC("file %wZ\n", &Segment->FileObject->FileName);
273 }
274 ULONG Entry = MiGetPageEntryCacheSectionSegment(Segment, &Offset);
275 DPRINT("Entry %x\n", Entry);
276 if (Entry &&
277 !IS_SWAP_FROM_SSE(Entry) &&
278 PFN_FROM_SSE(Entry) == MmGetPfnForProcess(Process, Address)) {
279 MiSetPageEntryCacheSectionSegment(Segment, &Offset, DIRTY_SSE(Entry));
280 }
281 MmSetPageProtect(Process, PAddress, PAGE_READWRITE);
282 MmUnlockCacheSectionSegment(Segment);
283 DPRINT("Done\n");
284 return STATUS_SUCCESS;
285 }
286 #if 0
287 else
288 {
289 DPRINT("Not supposed to be writable\n");
290 MmUnlockCacheSectionSegment(Segment);
291 return STATUS_ACCESS_VIOLATION;
292 }
293 #endif
294 }
295
296 if (!Required->Page[0])
297 {
298 SWAPENTRY SwapEntry;
299 if (MmIsPageSwapEntry(Process, Address))
300 {
301 MmGetPageFileMapping(Process, Address, &SwapEntry);
302 MmUnlockCacheSectionSegment(Segment);
303 if (SwapEntry == MM_WAIT_ENTRY)
304 return STATUS_SUCCESS + 1; // Wait ... somebody else is getting it right now
305 else
306 return STATUS_SUCCESS; // Nonwait swap entry ... handle elsewhere
307 }
308 Required->Page[1] = MmGetPfnForProcess(Process, Address);
309 Required->Consumer = MC_CACHE;
310 Required->Amount = 1;
311 Required->File = __FILE__;
312 Required->Line = __LINE__;
313 Required->DoAcquisition = MiGetOnePage;
314 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
315 MmUnlockCacheSectionSegment(Segment);
316 return STATUS_MORE_PROCESSING_REQUIRED;
317 }
318
319 NewPage = Required->Page[0];
320 OldPage = Required->Page[1];
321
322 DPRINT("Allocated page %x\n", NewPage);
323
324 /*
325 * Unshare the old page.
326 */
327 MmDeleteRmap(OldPage, Process, PAddress);
328
329 /*
330 * Copy the old page
331 */
332 DPRINT("Copying\n");
333 MiCopyPageToPage(NewPage, OldPage);
334
335 /*
336 * Set the PTE to point to the new page
337 */
338 Status = MmCreateVirtualMapping
339 (Process, Address, PAGE_READWRITE, &NewPage, 1);
340
341 if (!NT_SUCCESS(Status))
342 {
343 DPRINT1("MmCreateVirtualMapping failed, not out of memory\n");
344 ASSERT(FALSE);
345 MmUnlockCacheSectionSegment(Segment);
346 return(Status);
347 }
348
349 MmInsertRmap(NewPage, Process, PAddress);
350 MmReleasePageMemoryConsumer(MC_CACHE, OldPage);
351 MmUnlockCacheSectionSegment(Segment);
352
353 DPRINT("Address 0x%.8X\n", Address);
354 return(STATUS_SUCCESS);
355 }
356
357 KEVENT MmWaitPageEvent;
358
359 typedef struct _WORK_QUEUE_WITH_CONTEXT
360 {
361 WORK_QUEUE_ITEM WorkItem;
362 PMMSUPPORT AddressSpace;
363 PMEMORY_AREA MemoryArea;
364 PMM_REQUIRED_RESOURCES Required;
365 NTSTATUS Status;
366 KEVENT Wait;
367 AcquireResource DoAcquisition;
368 } WORK_QUEUE_WITH_CONTEXT, *PWORK_QUEUE_WITH_CONTEXT;
369
370 VOID
371 NTAPI
372 MmpFaultWorker
373 (PWORK_QUEUE_WITH_CONTEXT WorkItem)
374 {
375 DPRINT("Calling work\n");
376 WorkItem->Status =
377 WorkItem->Required->DoAcquisition
378 (WorkItem->AddressSpace,
379 WorkItem->MemoryArea,
380 WorkItem->Required);
381 DPRINT("Status %x\n", WorkItem->Status);
382 KeSetEvent(&WorkItem->Wait, IO_NO_INCREMENT, FALSE);
383 }
384
385 NTSTATUS
386 NTAPI
387 MmpSectionAccessFaultInner
388 (KPROCESSOR_MODE Mode,
389 PMMSUPPORT AddressSpace,
390 ULONG_PTR Address,
391 BOOLEAN FromMdl,
392 PETHREAD Thread)
393 {
394 MEMORY_AREA* MemoryArea;
395 NTSTATUS Status;
396 BOOLEAN Locked = FromMdl;
397 MM_REQUIRED_RESOURCES Resources = { 0 };
398
399 DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
400
401 if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
402 {
403 DPRINT1("Page fault at high IRQL was %d\n", KeGetCurrentIrql());
404 return(STATUS_UNSUCCESSFUL);
405 }
406
407 /*
408 * Find the memory area for the faulting address
409 */
410 if (Address >= (ULONG_PTR)MmSystemRangeStart)
411 {
412 /*
413 * Check permissions
414 */
415 if (Mode != KernelMode)
416 {
417 DPRINT("MmAccessFault(Mode %d, Address %x)\n", Mode, Address);
418 return(STATUS_ACCESS_VIOLATION);
419 }
420 AddressSpace = MmGetKernelAddressSpace();
421 }
422 else
423 {
424 AddressSpace = &PsGetCurrentProcess()->Vm;
425 }
426
427 if (!FromMdl)
428 {
429 MmLockAddressSpace(AddressSpace);
430 }
431
432 do
433 {
434 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
435 if (MemoryArea == NULL ||
436 MemoryArea->DeleteInProgress)
437 {
438 if (!FromMdl)
439 {
440 MmUnlockAddressSpace(AddressSpace);
441 }
442 DPRINT("Address: %x\n", Address);
443 return (STATUS_ACCESS_VIOLATION);
444 }
445
446 DPRINT
447 ("Type %x (%x -> %x)\n",
448 MemoryArea->Type,
449 MemoryArea->StartingAddress,
450 MemoryArea->EndingAddress);
451
452 Resources.DoAcquisition = NULL;
453
454 // Note: fault handlers are called with address space locked
455 // We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
456 Status = MiCowCacheSectionPage
457 (AddressSpace, MemoryArea, (PVOID)Address, Locked, &Resources);
458
459 if (!FromMdl)
460 {
461 MmUnlockAddressSpace(AddressSpace);
462 }
463
464 if (Status == STATUS_SUCCESS + 1)
465 {
466 // Wait page ...
467 DPRINT("Waiting for %x\n", Address);
468 MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
469 DPRINT("Restarting fault %x\n", Address);
470 Status = STATUS_MM_RESTART_OPERATION;
471 }
472 else if (Status == STATUS_MM_RESTART_OPERATION)
473 {
474 // Clean slate
475 RtlZeroMemory(&Resources, sizeof(Resources));
476 }
477 else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
478 {
479 if (Thread->ActiveFaultCount > 0)
480 {
481 WORK_QUEUE_WITH_CONTEXT Context = { };
482 DPRINT("Already fault handling ... going to work item (%x)\n", Address);
483 Context.AddressSpace = AddressSpace;
484 Context.MemoryArea = MemoryArea;
485 Context.Required = &Resources;
486 KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
487 ExInitializeWorkItem(&Context.WorkItem, (PWORKER_THREAD_ROUTINE)MmpFaultWorker, &Context);
488 DPRINT("Queue work item\n");
489 ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
490 DPRINT("Wait\n");
491 KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
492 Status = Context.Status;
493 DPRINT("Status %x\n", Status);
494 }
495 else
496 {
497 Status = Resources.DoAcquisition(AddressSpace, MemoryArea, &Resources);
498 }
499
500 if (NT_SUCCESS(Status))
501 {
502 Status = STATUS_MM_RESTART_OPERATION;
503 }
504 }
505
506 if (!FromMdl)
507 {
508 MmLockAddressSpace(AddressSpace);
509 }
510 }
511 while (Status == STATUS_MM_RESTART_OPERATION);
512
513 if (!NT_SUCCESS(Status) && MemoryArea->Type == 1)
514 {
515 DPRINT1("Completed page fault handling %x %x\n", Address, Status);
516 DPRINT1
517 ("Type %x (%x -> %x)\n",
518 MemoryArea->Type,
519 MemoryArea->StartingAddress,
520 MemoryArea->EndingAddress);
521 }
522
523 if (!FromMdl)
524 {
525 MmUnlockAddressSpace(AddressSpace);
526 }
527
528 return(Status);
529 }
530
531 NTSTATUS
532 NTAPI
533 MmAccessFaultCacheSection
534 (KPROCESSOR_MODE Mode,
535 ULONG_PTR Address,
536 BOOLEAN FromMdl)
537 {
538 PETHREAD Thread;
539 PMMSUPPORT AddressSpace;
540 NTSTATUS Status;
541
542 DPRINT("MmpAccessFault(Mode %d, Address %x)\n", Mode, Address);
543
544 Thread = PsGetCurrentThread();
545
546 if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
547 {
548 DPRINT1("Page fault at high IRQL %d, address %x\n", KeGetCurrentIrql(), Address);
549 return(STATUS_UNSUCCESSFUL);
550 }
551
552 /*
553 * Find the memory area for the faulting address
554 */
555 if (Address >= (ULONG_PTR)MmSystemRangeStart)
556 {
557 /*
558 * Check permissions
559 */
560 if (Mode != KernelMode)
561 {
562 DPRINT1("Address: %x:%x\n", PsGetCurrentProcess(), Address);
563 return(STATUS_ACCESS_VIOLATION);
564 }
565 AddressSpace = MmGetKernelAddressSpace();
566 }
567 else
568 {
569 AddressSpace = &PsGetCurrentProcess()->Vm;
570 }
571
572 Thread->ActiveFaultCount++;
573 Status = MmpSectionAccessFaultInner(Mode, AddressSpace, Address, FromMdl, Thread);
574 Thread->ActiveFaultCount--;
575
576 return(Status);
577 }
578
579 NTSTATUS
580 NTAPI
581 MmNotPresentFaultCacheSectionInner
582 (KPROCESSOR_MODE Mode,
583 PMMSUPPORT AddressSpace,
584 ULONG_PTR Address,
585 BOOLEAN FromMdl,
586 PETHREAD Thread)
587 {
588 BOOLEAN Locked = FromMdl;
589 PMEMORY_AREA MemoryArea;
590 MM_REQUIRED_RESOURCES Resources = { 0 };
591 NTSTATUS Status = STATUS_SUCCESS;
592
593 if (!FromMdl)
594 {
595 MmLockAddressSpace(AddressSpace);
596 }
597
598 /*
599 * Call the memory area specific fault handler
600 */
601 do
602 {
603 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, (PVOID)Address);
604 if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
605 {
606 Status = STATUS_ACCESS_VIOLATION;
607 if (MemoryArea)
608 {
609 DPRINT1("Type %x DIP %x\n", MemoryArea->Type, MemoryArea->DeleteInProgress);
610 }
611 else
612 {
613 DPRINT1("No memory area\n");
614 }
615 DPRINT1("Process %x, Address %x\n", MmGetAddressSpaceOwner(AddressSpace), Address);
616 break;
617 }
618
619 DPRINTC
620 ("Type %x (%x -> %x -> %x) in %x\n",
621 MemoryArea->Type,
622 MemoryArea->StartingAddress,
623 Address,
624 MemoryArea->EndingAddress,
625 PsGetCurrentThread());
626
627 Resources.DoAcquisition = NULL;
628
629 // Note: fault handlers are called with address space locked
630 // We return STATUS_MORE_PROCESSING_REQUIRED if anything is needed
631
632 Status = MmNotPresentFaultCachePage
633 (AddressSpace, MemoryArea, (PVOID)Address, Locked, &Resources);
634
635 if (!FromMdl)
636 {
637 MmUnlockAddressSpace(AddressSpace);
638 }
639
640 if (Status == STATUS_SUCCESS)
641 {
642 ; // Nothing
643 }
644 else if (Status == STATUS_SUCCESS + 1)
645 {
646 // Wait page ...
647 DPRINT("Waiting for %x\n", Address);
648 MiWaitForPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
649 DPRINT("Done waiting for %x\n", Address);
650 Status = STATUS_MM_RESTART_OPERATION;
651 }
652 else if (Status == STATUS_MM_RESTART_OPERATION)
653 {
654 // Clean slate
655 DPRINT("Clear resource\n");
656 RtlZeroMemory(&Resources, sizeof(Resources));
657 }
658 else if (Status == STATUS_MORE_PROCESSING_REQUIRED)
659 {
660 if (Thread->ActiveFaultCount > 1)
661 {
662 WORK_QUEUE_WITH_CONTEXT Context = { };
663 DPRINTC("Already fault handling ... going to work item (%x)\n", Address);
664 Context.AddressSpace = AddressSpace;
665 Context.MemoryArea = MemoryArea;
666 Context.Required = &Resources;
667 KeInitializeEvent(&Context.Wait, NotificationEvent, FALSE);
668 ExInitializeWorkItem(&Context.WorkItem, (PWORKER_THREAD_ROUTINE)MmpFaultWorker, &Context);
669 DPRINT("Queue work item\n");
670 ExQueueWorkItem(&Context.WorkItem, DelayedWorkQueue);
671 DPRINT("Wait\n");
672 KeWaitForSingleObject(&Context.Wait, 0, KernelMode, FALSE, NULL);
673 Status = Context.Status;
674 DPRINTC("Status %x\n", Status);
675 }
676 else
677 {
678 DPRINT("DoAcquisition %x\n", Resources.DoAcquisition);
679 Status = Resources.DoAcquisition
680 (AddressSpace, MemoryArea, &Resources);
681 DPRINT("DoAcquisition %x -> %x\n", Resources.DoAcquisition, Status);
682 }
683
684 if (NT_SUCCESS(Status))
685 {
686 Status = STATUS_MM_RESTART_OPERATION;
687 }
688 }
689 else if (NT_SUCCESS(Status))
690 {
691 ASSERT(FALSE);
692 }
693
694 if (!FromMdl)
695 {
696 MmLockAddressSpace(AddressSpace);
697 }
698 }
699 while (Status == STATUS_MM_RESTART_OPERATION);
700
701 DPRINTC("Completed page fault handling: %x:%x %x\n", MmGetAddressSpaceOwner(AddressSpace), Address, Status);
702 if (!FromMdl)
703 {
704 MmUnlockAddressSpace(AddressSpace);
705 }
706
707 MiSetPageEvent(MmGetAddressSpaceOwner(AddressSpace), Address);
708 DPRINT("Done %x\n", Status);
709
710 return Status;
711 }
712
713 NTSTATUS
714 NTAPI
715 MmNotPresentFaultCacheSection
716 (KPROCESSOR_MODE Mode,
717 ULONG_PTR Address,
718 BOOLEAN FromMdl)
719 {
720 PETHREAD Thread;
721 PMMSUPPORT AddressSpace;
722 NTSTATUS Status;
723
724 Address &= ~(PAGE_SIZE - 1);
725 DPRINT("MmNotPresentFault(Mode %d, Address %x)\n", Mode, Address);
726
727 Thread = PsGetCurrentThread();
728
729 if (KeGetCurrentIrql() >= DISPATCH_LEVEL)
730 {
731 DPRINT1("Page fault at high IRQL %d, address %x\n", KeGetCurrentIrql(), Address);
732 ASSERT(FALSE);
733 return(STATUS_UNSUCCESSFUL);
734 }
735
736 /*
737 * Find the memory area for the faulting address
738 */
739 if (Address >= (ULONG_PTR)MmSystemRangeStart)
740 {
741 /*
742 * Check permissions
743 */
744 if (Mode != KernelMode)
745 {
746 DPRINTC("Address: %x\n", Address);
747 return(STATUS_ACCESS_VIOLATION);
748 }
749 AddressSpace = MmGetKernelAddressSpace();
750 }
751 else
752 {
753 AddressSpace = &PsGetCurrentProcess()->Vm;
754 }
755
756 Thread->ActiveFaultCount++;
757 Status = MmNotPresentFaultCacheSectionInner
758 (Mode, AddressSpace, Address, FromMdl, Thread);
759 Thread->ActiveFaultCount--;
760
761 ASSERT(Status != STATUS_UNSUCCESSFUL);
762 ASSERT(Status != STATUS_INVALID_PARAMETER);
763 DPRINT("MmAccessFault %x:%x -> %x\n", MmGetAddressSpaceOwner(AddressSpace), Address, Status);
764
765 return(Status);
766 }