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