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