- Fix KiDispatchException to unmask KI_EXCEPTION_INTERNAL when setting the exception...
[reactos.git] / reactos / ntoskrnl / mm / pagefile.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/pagefile.c
22 * PURPOSE: Paging file functions
23 * PROGRAMMER: David Welch (welch@mcmail.com)
24 * UPDATE HISTORY:
25 * Created 22/05/98
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ntoskrnl.h>
31 #define NDEBUG
32 #include <internal/debug.h>
33
34 #if defined (ALLOC_PRAGMA)
35 #pragma alloc_text(INIT, MmInitPagingFile)
36 #endif
37
38 PVOID
39 NTAPI
40 MiFindExportedRoutineByName(IN PVOID DllBase,
41 IN PANSI_STRING ExportName);
42
43 /* TYPES *********************************************************************/
44
45 typedef struct _PAGINGFILE
46 {
47 LIST_ENTRY PagingFileListEntry;
48 PFILE_OBJECT FileObject;
49 LARGE_INTEGER MaximumSize;
50 LARGE_INTEGER CurrentSize;
51 ULONG FreePages;
52 ULONG UsedPages;
53 PULONG AllocMap;
54 KSPIN_LOCK AllocMapLock;
55 ULONG AllocMapSize;
56 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
57 }
58 PAGINGFILE, *PPAGINGFILE;
59
60 typedef struct _RETRIEVEL_DESCRIPTOR_LIST
61 {
62 struct _RETRIEVEL_DESCRIPTOR_LIST* Next;
63 RETRIEVAL_POINTERS_BUFFER RetrievalPointers;
64 }
65 RETRIEVEL_DESCRIPTOR_LIST, *PRETRIEVEL_DESCRIPTOR_LIST;
66
67 /* GLOBALS *******************************************************************/
68
69 #define PAIRS_PER_RUN (1024)
70
71 #define MAX_PAGING_FILES (32)
72
73 /* List of paging files, both used and free */
74 static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
75
76 /* Lock for examining the list of paging files */
77 static KSPIN_LOCK PagingFileListLock;
78
79 /* Number of paging files */
80 static ULONG MiPagingFileCount;
81
82 /* Number of pages that are available for swapping */
83 ULONG MiFreeSwapPages;
84
85 /* Number of pages that have been allocated for swapping */
86 ULONG MiUsedSwapPages;
87
88 /*
89 * Number of pages that have been reserved for swapping but not yet allocated
90 */
91 static ULONG MiReservedSwapPages;
92
93 /*
94 * Ratio between reserved and available swap pages, e.g. setting this to five
95 * forces one swap page to be available for every five swap pages that are
96 * reserved. Setting this to zero turns off commit checking altogether.
97 */
98 #define MM_PAGEFILE_COMMIT_RATIO (1)
99
100 /*
101 * Number of pages that can be used for potentially swapable memory without
102 * pagefile space being reserved. The intention is that this allows smss
103 * to start up and create page files while ordinarily having a commit
104 * ratio of one.
105 */
106 #define MM_PAGEFILE_COMMIT_GRACE (256)
107
108 static PVOID MmCoreDumpPageFrame = NULL;
109 static ULONG MmCoreDumpSize;
110 static DUMP_POINTERS MmCoreDumpPointers;
111 static PMM_CORE_DUMP_FUNCTIONS MmCoreDumpFunctions;
112 static ULONG MmCoreDumpPageFile = 0xFFFFFFFF;
113 static ROS_QUERY_LCN_MAPPING MmCoreDumpLcnMapping;
114
115 ULONG MmCoreDumpType = MM_CORE_DUMP_TYPE_NONE;
116
117 /*
118 * Translate between a swap entry and a file and offset pair.
119 */
120 #define FILE_FROM_ENTRY(i) ((i) >> 24)
121 #define OFFSET_FROM_ENTRY(i) (((i) & 0xffffff) - 1)
122 #define ENTRY_FROM_FILE_OFFSET(i, j) (((i) << 24) | ((j) + 1))
123
124 static BOOLEAN MmSwapSpaceMessage = FALSE;
125
126 /* FUNCTIONS *****************************************************************/
127
128 BOOLEAN
129 STDCALL
130 MmIsFileAPagingFile(PFILE_OBJECT FileObject)
131 {
132 ULONG i;
133
134 /* Loop through all the paging files */
135 for (i = 0; i < MiPagingFileCount; i++)
136 {
137 /* Check if this is one of them */
138 if (PagingFileList[i]->FileObject == FileObject) return TRUE;
139 }
140
141 /* Nothing found */
142 return FALSE;
143 }
144
145 VOID
146 NTAPI
147 MmShowOutOfSpaceMessagePagingFile(VOID)
148 {
149 if (!MmSwapSpaceMessage)
150 {
151 DPRINT1("MM: Out of swap space.\n");
152 MmSwapSpaceMessage = TRUE;
153 }
154 }
155
156 LARGE_INTEGER static
157 MmGetOffsetPageFile(PRETRIEVAL_POINTERS_BUFFER RetrievalPointers, LARGE_INTEGER Offset)
158 {
159 /* Simple binary search */
160 ULONG first, last, mid;
161 first = 0;
162 last = RetrievalPointers->ExtentCount - 1;
163 while (first <= last)
164 {
165 mid = (last - first) / 2 + first;
166 if (Offset.QuadPart < RetrievalPointers->Extents[mid].NextVcn.QuadPart)
167 {
168 if (mid == 0)
169 {
170 Offset.QuadPart += RetrievalPointers->Extents[0].Lcn.QuadPart - RetrievalPointers->StartingVcn.QuadPart;
171 return Offset;
172 }
173 else
174 {
175 if (Offset.QuadPart >= RetrievalPointers->Extents[mid-1].NextVcn.QuadPart)
176 {
177 Offset.QuadPart += RetrievalPointers->Extents[mid].Lcn.QuadPart - RetrievalPointers->Extents[mid-1].NextVcn.QuadPart;
178 return Offset;
179 }
180 last = mid - 1;
181 }
182 }
183 else
184 {
185 if (mid == RetrievalPointers->ExtentCount - 1)
186 {
187 break;
188 }
189 if (Offset.QuadPart < RetrievalPointers->Extents[mid+1].NextVcn.QuadPart)
190 {
191 Offset.QuadPart += RetrievalPointers->Extents[mid+1].Lcn.QuadPart - RetrievalPointers->Extents[mid].NextVcn.QuadPart;
192 return Offset;
193 }
194 first = mid + 1;
195 }
196 }
197 KEBUGCHECK(0);
198 #if defined(__GNUC__)
199
200 return (LARGE_INTEGER)0LL;
201 #else
202
203 {
204 const LARGE_INTEGER dummy =
205 {
206 0
207 };
208 return dummy;
209 }
210 #endif
211 }
212
213 NTSTATUS
214 NTAPI
215 MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_TYPE Page)
216 {
217 ULONG i, offset;
218 LARGE_INTEGER file_offset;
219 IO_STATUS_BLOCK Iosb;
220 NTSTATUS Status;
221 KEVENT Event;
222 UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
223 PMDL Mdl = (PMDL)MdlBase;
224
225 DPRINT("MmWriteToSwapPage\n");
226
227 if (SwapEntry == 0)
228 {
229 KEBUGCHECK(0);
230 return(STATUS_UNSUCCESSFUL);
231 }
232
233 i = FILE_FROM_ENTRY(SwapEntry);
234 offset = OFFSET_FROM_ENTRY(SwapEntry);
235
236 if (i >= MAX_PAGING_FILES)
237 {
238 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
239 KEBUGCHECK(0);
240 }
241 if (PagingFileList[i]->FileObject == NULL ||
242 PagingFileList[i]->FileObject->DeviceObject == NULL)
243 {
244 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
245 KEBUGCHECK(0);
246 }
247
248 MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
249 MmBuildMdlFromPages(Mdl, &Page);
250
251 file_offset.QuadPart = offset * PAGE_SIZE;
252 file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers, file_offset);
253
254 KeInitializeEvent(&Event, NotificationEvent, FALSE);
255 Status = IoSynchronousPageWrite(PagingFileList[i]->FileObject,
256 Mdl,
257 &file_offset,
258 &Event,
259 &Iosb);
260 if (Status == STATUS_PENDING)
261 {
262 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
263 Status = Iosb.Status;
264 }
265 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
266 return(Status);
267 }
268
269 NTSTATUS
270 NTAPI
271 MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_TYPE Page)
272 {
273 ULONG i, offset;
274 LARGE_INTEGER file_offset;
275 IO_STATUS_BLOCK Iosb;
276 NTSTATUS Status;
277 KEVENT Event;
278 UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
279 PMDL Mdl = (PMDL)MdlBase;
280
281 DPRINT("MmReadFromSwapPage\n");
282
283 if (SwapEntry == 0)
284 {
285 KEBUGCHECK(0);
286 return(STATUS_UNSUCCESSFUL);
287 }
288
289 i = FILE_FROM_ENTRY(SwapEntry);
290 offset = OFFSET_FROM_ENTRY(SwapEntry);
291
292 if (i >= MAX_PAGING_FILES)
293 {
294 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
295 KEBUGCHECK(0);
296 }
297 if (PagingFileList[i]->FileObject == NULL ||
298 PagingFileList[i]->FileObject->DeviceObject == NULL)
299 {
300 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
301 KEBUGCHECK(0);
302 }
303
304 MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
305 MmBuildMdlFromPages(Mdl, &Page);
306
307 file_offset.QuadPart = offset * PAGE_SIZE;
308 file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers, file_offset);
309
310 KeInitializeEvent(&Event, NotificationEvent, FALSE);
311 Status = IoPageRead(PagingFileList[i]->FileObject,
312 Mdl,
313 &file_offset,
314 &Event,
315 &Iosb);
316 if (Status == STATUS_PENDING)
317 {
318 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
319 Status = Iosb.Status;
320 }
321 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
322 return(Status);
323 }
324
325 extern BOOLEAN PagingReady;
326
327 VOID
328 INIT_FUNCTION
329 NTAPI
330 MmInitPagingFile(VOID)
331 {
332 ULONG i;
333
334 KeInitializeSpinLock(&PagingFileListLock);
335
336 MiFreeSwapPages = 0;
337 MiUsedSwapPages = 0;
338 MiReservedSwapPages = 0;
339
340 for (i = 0; i < MAX_PAGING_FILES; i++)
341 {
342 PagingFileList[i] = NULL;
343 }
344 MiPagingFileCount = 0;
345
346 /*
347 * Initialize the crash dump support.
348 */
349 if (MmCoreDumpType != MM_CORE_DUMP_TYPE_NONE)
350 {
351 MmCoreDumpPageFrame = MmAllocateSection(PAGE_SIZE, NULL);
352 if (MmCoreDumpType == MM_CORE_DUMP_TYPE_FULL)
353 {
354 MmCoreDumpSize = MmStats.NrTotalPages * 4096 + 1024 * 1024;
355 }
356 else
357 {
358 MmCoreDumpSize = 1024 * 1024;
359 }
360 }
361 }
362
363 BOOLEAN
364 NTAPI
365 MmReserveSwapPages(ULONG Nr)
366 {
367 KIRQL oldIrql;
368 ULONG MiAvailSwapPages;
369
370 if (!PagingReady)
371 {
372 DPRINT1("PAGING USED TOO SOON!!!\n");
373 while (TRUE);
374 }
375 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
376 MiAvailSwapPages =
377 (MiFreeSwapPages * MM_PAGEFILE_COMMIT_RATIO) + MM_PAGEFILE_COMMIT_GRACE;
378 MiReservedSwapPages = MiReservedSwapPages + Nr;
379 if (MM_PAGEFILE_COMMIT_RATIO != 0 && MiAvailSwapPages < MiReservedSwapPages)
380 {
381 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
382 return(FALSE);
383 }
384 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
385 return(TRUE);
386 }
387
388 VOID
389 NTAPI
390 MmDereserveSwapPages(ULONG Nr)
391 {
392 KIRQL oldIrql;
393
394 if (!PagingReady)
395 {
396 DPRINT1("PAGING USED TOO SOON!!!\n");
397 while (TRUE);
398 }
399 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
400 MiReservedSwapPages = MiReservedSwapPages - Nr;
401 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
402 }
403
404 static ULONG
405 MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
406 {
407 KIRQL oldIrql;
408 ULONG i, j;
409
410 if (!PagingReady)
411 {
412 DPRINT1("PAGING USED TOO SOON!!!\n");
413 while (TRUE);
414 }
415 KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
416
417 for (i = 0; i < PagingFile->AllocMapSize; i++)
418 {
419 for (j = 0; j < 32; j++)
420 {
421 if (!(PagingFile->AllocMap[i] & (1 << j)))
422 {
423 PagingFile->AllocMap[i] |= (1 << j);
424 PagingFile->UsedPages++;
425 PagingFile->FreePages--;
426 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
427 return((i * 32) + j);
428 }
429 }
430 }
431
432 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
433 return(0xFFFFFFFF);
434 }
435
436 VOID
437 NTAPI
438 MmFreeSwapPage(SWAPENTRY Entry)
439 {
440 ULONG i;
441 ULONG off;
442 KIRQL oldIrql;
443
444 if (!PagingReady)
445 {
446 DPRINT1("PAGING USED TOO SOON!!!\n");
447 while (TRUE);
448 }
449 i = FILE_FROM_ENTRY(Entry);
450 off = OFFSET_FROM_ENTRY(Entry);
451
452 if (i >= MAX_PAGING_FILES)
453 {
454 DPRINT1("Bad swap entry 0x%.8X\n", Entry);
455 KEBUGCHECK(0);
456 }
457
458 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
459 if (PagingFileList[i] == NULL)
460 {
461 KEBUGCHECK(0);
462 }
463 KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
464
465 PagingFileList[i]->AllocMap[off >> 5] &= (~(1 << (off % 32)));
466
467 PagingFileList[i]->FreePages++;
468 PagingFileList[i]->UsedPages--;
469
470 MiFreeSwapPages++;
471 MiUsedSwapPages--;
472
473 KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
474 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
475 }
476
477 BOOLEAN
478 NTAPI
479 MmIsAvailableSwapPage(VOID)
480 {
481 return(MiFreeSwapPages > 0);
482 }
483
484 SWAPENTRY
485 NTAPI
486 MmAllocSwapPage(VOID)
487 {
488 KIRQL oldIrql;
489 ULONG i;
490 ULONG off;
491 SWAPENTRY entry;
492
493 if (!PagingReady)
494 {
495 DPRINT1("PAGING USED TOO SOON!!!\n");
496 while (TRUE);
497 }
498 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
499
500 if (MiFreeSwapPages == 0)
501 {
502 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
503 return(0);
504 }
505
506 for (i = 0; i < MAX_PAGING_FILES; i++)
507 {
508 if (PagingFileList[i] != NULL &&
509 PagingFileList[i]->FreePages >= 1)
510 {
511 off = MiAllocPageFromPagingFile(PagingFileList[i]);
512 if (off == 0xFFFFFFFF)
513 {
514 KEBUGCHECK(0);
515 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
516 return(STATUS_UNSUCCESSFUL);
517 }
518 MiUsedSwapPages++;
519 MiFreeSwapPages--;
520 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
521
522 entry = ENTRY_FROM_FILE_OFFSET(i, off);
523 return(entry);
524 }
525 }
526
527 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
528 KEBUGCHECK(0);
529 return(0);
530 }
531
532 static PRETRIEVEL_DESCRIPTOR_LIST FASTCALL
533 MmAllocRetrievelDescriptorList(ULONG Pairs)
534 {
535 ULONG Size;
536 PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
537
538 Size = sizeof(RETRIEVEL_DESCRIPTOR_LIST) + Pairs * 2 * sizeof(LARGE_INTEGER);
539 RetDescList = ExAllocatePool(NonPagedPool, Size);
540 if (RetDescList)
541 {
542 RtlZeroMemory(RetDescList, Size);
543 }
544
545 return RetDescList;
546 }
547
548 NTSTATUS STDCALL
549 MmDumpToPagingFile(ULONG BugCode,
550 ULONG BugCodeParameter1,
551 ULONG BugCodeParameter2,
552 ULONG BugCodeParameter3,
553 ULONG BugCodeParameter4,
554 PKTRAP_FRAME TrapFrame)
555 {
556 PMM_CORE_DUMP_HEADER Headers;
557 NTSTATUS Status;
558 UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
559 PMDL Mdl = (PMDL)MdlBase;
560 PETHREAD Thread = PsGetCurrentThread();
561 ULONG_PTR StackSize;
562 PULONG MdlMap;
563 LONGLONG NextOffset = 0;
564 ULONG i;
565 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
566 LARGE_INTEGER DiskOffset;
567
568 if (MmCoreDumpPageFile == 0xFFFFFFFF)
569 {
570 return(STATUS_UNSUCCESSFUL);
571 }
572
573 DbgPrint("\nMM: Dumping core: ");
574
575 /* Prepare the dump headers. */
576 Headers = (PMM_CORE_DUMP_HEADER)MmCoreDumpPageFrame;
577 Headers->Magic = MM_CORE_DUMP_HEADER_MAGIC;
578 Headers->Version = MM_CORE_DUMP_HEADER_VERSION;
579 Headers->Type = MmCoreDumpType;
580 if (TrapFrame != NULL)
581 {
582 if (!(TrapFrame->EFlags & (1 << 17)))
583 {
584 memcpy(&Headers->TrapFrame, TrapFrame,
585 sizeof(KTRAP_FRAME) - (4 * sizeof(ULONG)));
586 }
587 else
588 {
589 memcpy(&Headers->TrapFrame, TrapFrame, sizeof(KTRAP_FRAME));
590 }
591 }
592 Headers->BugCheckCode = BugCode;
593 Headers->BugCheckParameters[0] = BugCodeParameter1;
594 Headers->BugCheckParameters[1] = BugCodeParameter2;
595 Headers->BugCheckParameters[2] = BugCodeParameter3;
596 Headers->BugCheckParameters[3] = BugCodeParameter4;
597 Headers->FaultingStackBase = (PVOID)Thread->Tcb.StackLimit;
598 Headers->FaultingStackSize =
599 StackSize = (ULONG_PTR)Thread->Tcb.StackBase - (ULONG_PTR)Thread->Tcb.StackLimit;
600 Headers->PhysicalMemorySize = MmStats.NrTotalPages * PAGE_SIZE;
601
602 /* Initialize the dump device. */
603 Status = MmCoreDumpFunctions->DumpInit();
604 if (!NT_SUCCESS(Status))
605 {
606 DPRINT1("MM: Failed to initialize core dump device.\n");
607 return(Status);
608 }
609
610 /* Initialize the MDL. */
611 MmInitializeMdl(Mdl, MmCoreDumpPageFrame, PAGE_SIZE);
612 Mdl->MdlFlags = MDL_PAGES_LOCKED|MDL_IO_PAGE_READ|MDL_SOURCE_IS_NONPAGED_POOL;
613 MdlMap = (PULONG)(Mdl + 1);
614
615
616 /* Initialize the retrieval offsets. */
617 RetrievalPointers = PagingFileList[MmCoreDumpPageFile]->RetrievalPointers;
618
619 /* Dump the header. */
620 MdlMap[0] = (ULONG)(MmGetPhysicalAddress(MmCoreDumpPageFrame).QuadPart >> PAGE_SHIFT);
621 #if defined(__GNUC__)
622
623 DiskOffset = MmGetOffsetPageFile(RetrievalPointers, (LARGE_INTEGER)0LL);
624 #else
625
626 {
627 const LARGE_INTEGER dummy =
628 {
629 0
630 };
631 DiskOffset = MmGetOffsetPageFile(RetrievalPointers, dummy);
632 }
633 #endif
634 DiskOffset.QuadPart += MmCoreDumpLcnMapping.LcnDiskOffset.QuadPart;
635 Status = MmCoreDumpFunctions->DumpWrite(DiskOffset, Mdl);
636 if (!NT_SUCCESS(Status))
637 {
638 DPRINT1("MM: Failed to write core dump header\n.");
639 return(Status);
640 }
641 NextOffset += PAGE_SIZE;
642 ;
643 DbgPrint("00");
644
645
646 /* Write out the contents of physical memory. */
647 if (MmCoreDumpType == MM_CORE_DUMP_TYPE_FULL)
648 {
649 for (i = 0; i < MmStats.NrTotalPages; i++)
650 {
651 MdlMap[0] = i;
652 MmCreateVirtualMappingForKernel(MmCoreDumpPageFrame,
653 PAGE_READWRITE,
654 MdlMap,
655 1);
656 #if defined(__GNUC__)
657
658 DiskOffset = MmGetOffsetPageFile(RetrievalPointers,
659 (LARGE_INTEGER)NextOffset);
660 #else
661
662 {
663 LARGE_INTEGER dummy;
664 dummy.QuadPart = NextOffset;
665 DiskOffset = MmGetOffsetPageFile(RetrievalPointers, dummy);
666 }
667 #endif
668 DiskOffset.QuadPart += MmCoreDumpLcnMapping.LcnDiskOffset.QuadPart;
669 Status = MmCoreDumpFunctions->DumpWrite(DiskOffset, Mdl);
670 MmRawDeleteVirtualMapping(MmCoreDumpPageFrame);
671 if (!NT_SUCCESS(Status))
672 {
673 DPRINT1("MM: Failed to write page to core dump.\n");
674 return(Status);
675 }
676 if ((i % ((1024*1024) / PAGE_SIZE)) == 0)
677 {
678 DbgPrint("\b\b%.2d", i / ((1024*1024)/PAGE_SIZE));
679 }
680 NextOffset += PAGE_SIZE;
681 }
682 }
683
684 DbgPrint("\n");
685 MmCoreDumpFunctions->DumpFinish();
686 return(STATUS_SUCCESS);
687 }
688
689 NTSTATUS STDCALL
690 MmInitializeCrashDump(HANDLE PageFileHandle, ULONG PageFileNum)
691 {
692 PFILE_OBJECT PageFile;
693 PDEVICE_OBJECT PageFileDevice;
694 NTSTATUS Status;
695 PIRP Irp;
696 KEVENT Event;
697 IO_STATUS_BLOCK Iosb;
698 UNICODE_STRING DiskDumpName = RTL_CONSTANT_STRING(L"DiskDump");
699 ANSI_STRING ProcName;
700 PIO_STACK_LOCATION StackPtr;
701 PLDR_DATA_TABLE_ENTRY ModuleObject = NULL;
702
703 Status = ZwFsControlFile(PageFileHandle,
704 0,
705 NULL,
706 NULL,
707 &Iosb,
708 FSCTL_ROS_QUERY_LCN_MAPPING,
709 NULL,
710 0,
711 &MmCoreDumpLcnMapping,
712 sizeof(ROS_QUERY_LCN_MAPPING));
713 if (!NT_SUCCESS(Status) ||
714 Iosb.Information != sizeof(ROS_QUERY_LCN_MAPPING))
715 {
716 return(Status);
717 }
718
719 /* Get the underlying storage device. */
720 Status =
721 ObReferenceObjectByHandle(PageFileHandle,
722 FILE_ALL_ACCESS,
723 NULL,
724 KernelMode,
725 (PVOID*)&PageFile,
726 NULL);
727 if (!NT_SUCCESS(Status))
728 {
729 return(Status);
730 }
731
732 PageFileDevice = PageFile->Vpb->RealDevice;
733
734 /* Get the dump pointers. */
735 KeInitializeEvent(&Event, NotificationEvent, FALSE);
736 Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_GET_DUMP_POINTERS,
737 PageFileDevice,
738 NULL,
739 0,
740 &MmCoreDumpPointers,
741 sizeof(MmCoreDumpPointers),
742 FALSE,
743 &Event,
744 &Iosb);
745 if(Irp == NULL)
746 {
747 ObDereferenceObject(PageFile);
748 return(STATUS_NO_MEMORY);// tMk - is this correct return code ???
749 }
750
751 StackPtr = IoGetNextIrpStackLocation(Irp);
752 StackPtr->FileObject = PageFile;
753 StackPtr->DeviceObject = PageFileDevice;
754 StackPtr->Parameters.DeviceIoControl.InputBufferLength = 0;
755 StackPtr->Parameters.DeviceIoControl.OutputBufferLength = sizeof(MmCoreDumpPointers);
756
757 Status = IoCallDriver(PageFileDevice,Irp);
758 if (Status == STATUS_PENDING)
759 {
760 Status = KeWaitForSingleObject(&Event,
761 Executive,
762 KernelMode,
763 FALSE,
764 NULL);
765 }
766 if (Status != STATUS_SUCCESS ||
767 Iosb.Information != sizeof(MmCoreDumpPointers))
768 {
769 ObDereferenceObject(PageFile);
770 return(Status);
771 }
772
773 /* Load the diskdump driver. */
774 Status = MmLoadSystemImage(&DiskDumpName, NULL, NULL, 0, (PVOID)&ModuleObject, NULL);
775 if (ModuleObject == NULL)
776 {
777 return(STATUS_OBJECT_NAME_NOT_FOUND);
778 }
779 RtlInitAnsiString(&ProcName, "DiskDumpFunctions");
780 MmCoreDumpFunctions = MiFindExportedRoutineByName(ModuleObject->DllBase,
781 &ProcName);
782 if (!NT_SUCCESS(Status))
783 {
784 ObDereferenceObject(PageFile);
785 return(Status);
786 }
787
788 /* Prepare for disk dumping. */
789 Status = MmCoreDumpFunctions->DumpPrepare(PageFileDevice,
790 &MmCoreDumpPointers);
791 if (!NT_SUCCESS(Status))
792 {
793 ObDereferenceObject(PageFile);
794 return(Status);
795 }
796
797 MmCoreDumpPageFile = PageFileNum;
798 ObDereferenceObject(PageFile);
799 return(STATUS_SUCCESS);
800 }
801
802 NTSTATUS STDCALL
803 NtCreatePagingFile(IN PUNICODE_STRING FileName,
804 IN PLARGE_INTEGER InitialSize,
805 IN PLARGE_INTEGER MaximumSize,
806 IN ULONG Reserved)
807 {
808 NTSTATUS Status = STATUS_SUCCESS;
809 OBJECT_ATTRIBUTES ObjectAttributes;
810 HANDLE FileHandle;
811 IO_STATUS_BLOCK IoStatus;
812 PFILE_OBJECT FileObject;
813 PPAGINGFILE PagingFile;
814 KIRQL oldIrql;
815 ULONG AllocMapSize;
816 FILE_FS_SIZE_INFORMATION FsSizeInformation;
817 PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
818 PRETRIEVEL_DESCRIPTOR_LIST CurrentRetDescList;
819 ULONG i;
820 ULONG BytesPerAllocationUnit;
821 LARGE_INTEGER Vcn;
822 ULONG ExtentCount;
823 LARGE_INTEGER MaxVcn;
824 ULONG Count;
825 ULONG Size;
826 KPROCESSOR_MODE PreviousMode;
827 UNICODE_STRING CapturedFileName;
828 LARGE_INTEGER SafeInitialSize, SafeMaximumSize;
829
830 DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
831 FileName, InitialSize->QuadPart);
832
833 if (MiPagingFileCount >= MAX_PAGING_FILES)
834 {
835 return(STATUS_TOO_MANY_PAGING_FILES);
836 }
837
838 PreviousMode = ExGetPreviousMode();
839
840 if (PreviousMode != KernelMode)
841 {
842 _SEH_TRY
843 {
844 SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
845 SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
846 }
847 _SEH_HANDLE
848 {
849 Status = _SEH_GetExceptionCode();
850 }
851 _SEH_END;
852
853 if (!NT_SUCCESS(Status))
854 {
855 return Status;
856 }
857 }
858 else
859 {
860 SafeInitialSize = *InitialSize;
861 SafeMaximumSize = *MaximumSize;
862 }
863
864 /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
865 smaller than the maximum */
866 if (0 != SafeInitialSize.u.HighPart)
867 {
868 return STATUS_INVALID_PARAMETER_2;
869 }
870 if (0 != SafeMaximumSize.u.HighPart)
871 {
872 return STATUS_INVALID_PARAMETER_3;
873 }
874 if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
875 {
876 return STATUS_INVALID_PARAMETER_MIX;
877 }
878
879 Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
880 PreviousMode,
881 FileName);
882 if (!NT_SUCCESS(Status))
883 {
884 return(Status);
885 }
886
887 InitializeObjectAttributes(&ObjectAttributes,
888 &CapturedFileName,
889 0,
890 NULL,
891 NULL);
892
893 Status = IoCreateFile(&FileHandle,
894 FILE_ALL_ACCESS,
895 &ObjectAttributes,
896 &IoStatus,
897 NULL,
898 0,
899 0,
900 FILE_OPEN_IF,
901 FILE_SYNCHRONOUS_IO_NONALERT,
902 NULL,
903 0,
904 CreateFileTypeNone,
905 NULL,
906 SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
907
908 ReleaseCapturedUnicodeString(&CapturedFileName,
909 PreviousMode);
910 if (!NT_SUCCESS(Status))
911 {
912 return(Status);
913 }
914
915 Status = ZwQueryVolumeInformationFile(FileHandle,
916 &IoStatus,
917 &FsSizeInformation,
918 sizeof(FILE_FS_SIZE_INFORMATION),
919 FileFsSizeInformation);
920 if (!NT_SUCCESS(Status))
921 {
922 ZwClose(FileHandle);
923 return Status;
924 }
925
926 BytesPerAllocationUnit = FsSizeInformation.SectorsPerAllocationUnit *
927 FsSizeInformation.BytesPerSector;
928 /* FIXME: If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
929 * a problem if the paging file is fragmented. Suppose the first cluster
930 * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
931 * paging file but of another file. We can't write a complete page (4096
932 * bytes) to the physical location of cluster 3042 then. */
933 if (BytesPerAllocationUnit % PAGE_SIZE)
934 {
935 DPRINT1("BytesPerAllocationUnit %d is not a multiple of PAGE_SIZE %d\n",
936 BytesPerAllocationUnit, PAGE_SIZE);
937 ZwClose(FileHandle);
938 return STATUS_UNSUCCESSFUL;
939 }
940
941 Status = ZwSetInformationFile(FileHandle,
942 &IoStatus,
943 &SafeInitialSize,
944 sizeof(LARGE_INTEGER),
945 FileAllocationInformation);
946 if (!NT_SUCCESS(Status))
947 {
948 ZwClose(FileHandle);
949 return(Status);
950 }
951
952 Status = ObReferenceObjectByHandle(FileHandle,
953 FILE_ALL_ACCESS,
954 IoFileObjectType,
955 PreviousMode,
956 (PVOID*)&FileObject,
957 NULL);
958 if (!NT_SUCCESS(Status))
959 {
960 ZwClose(FileHandle);
961 return(Status);
962 }
963
964 CurrentRetDescList = RetDescList = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
965
966 if (CurrentRetDescList == NULL)
967 {
968 ObDereferenceObject(FileObject);
969 ZwClose(FileHandle);
970 return(STATUS_NO_MEMORY);
971 }
972
973 #if defined(__GNUC__)
974 Vcn.QuadPart = 0LL;
975 #else
976
977 Vcn.QuadPart = 0;
978 #endif
979
980 ExtentCount = 0;
981 MaxVcn.QuadPart = (SafeInitialSize.QuadPart + BytesPerAllocationUnit - 1) / BytesPerAllocationUnit;
982 while(1)
983 {
984 Status = ZwFsControlFile(FileHandle,
985 0,
986 NULL,
987 NULL,
988 &IoStatus,
989 FSCTL_GET_RETRIEVAL_POINTERS,
990 &Vcn,
991 sizeof(LARGE_INTEGER),
992 &CurrentRetDescList->RetrievalPointers,
993 sizeof(RETRIEVAL_POINTERS_BUFFER) + PAIRS_PER_RUN * 2 * sizeof(LARGE_INTEGER));
994 if (!NT_SUCCESS(Status))
995 {
996 while (RetDescList)
997 {
998 CurrentRetDescList = RetDescList;
999 RetDescList = RetDescList->Next;
1000 ExFreePool(CurrentRetDescList);
1001 }
1002 ObDereferenceObject(FileObject);
1003 ZwClose(FileHandle);
1004 return(Status);
1005 }
1006 ExtentCount += CurrentRetDescList->RetrievalPointers.ExtentCount;
1007 if (CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn.QuadPart < MaxVcn.QuadPart)
1008 {
1009 CurrentRetDescList->Next = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
1010 if (CurrentRetDescList->Next == NULL)
1011 {
1012 while (RetDescList)
1013 {
1014 CurrentRetDescList = RetDescList;
1015 RetDescList = RetDescList->Next;
1016 ExFreePool(CurrentRetDescList);
1017 }
1018 ObDereferenceObject(FileObject);
1019 ZwClose(FileHandle);
1020 return(STATUS_NO_MEMORY);
1021 }
1022 Vcn = CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn;
1023 CurrentRetDescList = CurrentRetDescList->Next;
1024 }
1025 else
1026 {
1027 break;
1028 }
1029 }
1030
1031 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
1032 if (PagingFile == NULL)
1033 {
1034 while (RetDescList)
1035 {
1036 CurrentRetDescList = RetDescList;
1037 RetDescList = RetDescList->Next;
1038 ExFreePool(CurrentRetDescList);
1039 }
1040 ObDereferenceObject(FileObject);
1041 ZwClose(FileHandle);
1042 return(STATUS_NO_MEMORY);
1043 }
1044
1045 RtlZeroMemory(PagingFile, sizeof(*PagingFile));
1046
1047 PagingFile->FileObject = FileObject;
1048 PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
1049 PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
1050 PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
1051 PagingFile->UsedPages = 0;
1052 KeInitializeSpinLock(&PagingFile->AllocMapLock);
1053
1054 AllocMapSize = (PagingFile->FreePages / 32) + 1;
1055 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
1056 AllocMapSize * sizeof(ULONG));
1057 PagingFile->AllocMapSize = AllocMapSize;
1058
1059 if (PagingFile->AllocMap == NULL)
1060 {
1061 while (RetDescList)
1062 {
1063 CurrentRetDescList = RetDescList;
1064 RetDescList = RetDescList->Next;
1065 ExFreePool(CurrentRetDescList);
1066 }
1067 ExFreePool(PagingFile);
1068 ObDereferenceObject(FileObject);
1069 ZwClose(FileHandle);
1070 return(STATUS_NO_MEMORY);
1071 }
1072 DPRINT("ExtentCount: %d\n", ExtentCount);
1073 Size = sizeof(RETRIEVAL_POINTERS_BUFFER) + ExtentCount * 2 * sizeof(LARGE_INTEGER);
1074 PagingFile->RetrievalPointers = ExAllocatePool(NonPagedPool, Size);
1075 if (PagingFile->RetrievalPointers == NULL)
1076 {
1077 while (RetDescList)
1078 {
1079 CurrentRetDescList = RetDescList;
1080 RetDescList = RetDescList->Next;
1081 ExFreePool(CurrentRetDescList);
1082 }
1083 ExFreePool(PagingFile->AllocMap);
1084 ExFreePool(PagingFile);
1085 ObDereferenceObject(FileObject);
1086 ZwClose(FileHandle);
1087 return(STATUS_NO_MEMORY);
1088 }
1089
1090 RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG));
1091 RtlZeroMemory(PagingFile->RetrievalPointers, Size);
1092
1093 Count = 0;
1094 PagingFile->RetrievalPointers->ExtentCount = ExtentCount;
1095 PagingFile->RetrievalPointers->StartingVcn = RetDescList->RetrievalPointers.StartingVcn;
1096 CurrentRetDescList = RetDescList;
1097 while (CurrentRetDescList)
1098 {
1099 memcpy(&PagingFile->RetrievalPointers->Extents[Count],
1100 CurrentRetDescList->RetrievalPointers.Extents,
1101 CurrentRetDescList->RetrievalPointers.ExtentCount * 2 * sizeof(LARGE_INTEGER));
1102 Count += CurrentRetDescList->RetrievalPointers.ExtentCount;
1103 RetDescList = CurrentRetDescList;
1104 CurrentRetDescList = CurrentRetDescList->Next;
1105 ExFreePool(RetDescList);
1106 }
1107
1108 if (PagingFile->RetrievalPointers->ExtentCount != ExtentCount ||
1109 PagingFile->RetrievalPointers->Extents[ExtentCount - 1].NextVcn.QuadPart != MaxVcn.QuadPart)
1110 {
1111 ExFreePool(PagingFile->RetrievalPointers);
1112 ExFreePool(PagingFile->AllocMap);
1113 ExFreePool(PagingFile);
1114 ObDereferenceObject(FileObject);
1115 ZwClose(FileHandle);
1116 return(STATUS_UNSUCCESSFUL);
1117 }
1118
1119 /*
1120 * Change the entries from lcn's to volume offset's.
1121 */
1122 PagingFile->RetrievalPointers->StartingVcn.QuadPart *= BytesPerAllocationUnit;
1123 for (i = 0; i < ExtentCount; i++)
1124 {
1125 PagingFile->RetrievalPointers->Extents[i].Lcn.QuadPart *= BytesPerAllocationUnit;
1126 PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *= BytesPerAllocationUnit;
1127 }
1128
1129 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
1130 for (i = 0; i < MAX_PAGING_FILES; i++)
1131 {
1132 if (PagingFileList[i] == NULL)
1133 {
1134 PagingFileList[i] = PagingFile;
1135 break;
1136 }
1137 }
1138 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
1139 MiPagingFileCount++;
1140 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
1141
1142 /* Check whether this pagefile can be a crash dump target. */
1143 if (MmCoreDumpType != MM_CORE_DUMP_TYPE_NONE &&
1144 PagingFile->CurrentSize.QuadPart >= MmCoreDumpSize &&
1145 MmCoreDumpPageFile == 0xFFFFFFFF)
1146 {
1147 MmInitializeCrashDump(FileHandle, i);
1148 }
1149 ZwClose(FileHandle);
1150
1151 MmSwapSpaceMessage = FALSE;
1152
1153 return(STATUS_SUCCESS);
1154 }
1155
1156 /* EOF */