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