On FAT16 partitions smaller than 128MB, the cluster size is 2048, which is
[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 if (PreviousMode != KernelMode)
813 {
814 _SEH_TRY
815 {
816 SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
817 SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
818 }
819 _SEH_HANDLE
820 {
821 Status = _SEH_GetExceptionCode();
822 }
823 _SEH_END;
824
825 if (!NT_SUCCESS(Status))
826 {
827 return Status;
828 }
829 }
830 else
831 {
832 SafeInitialSize = *InitialSize;
833 SafeMaximumSize = *MaximumSize;
834 }
835
836 /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
837 smaller than the maximum */
838 if (0 != SafeInitialSize.u.HighPart)
839 {
840 return STATUS_INVALID_PARAMETER_2;
841 }
842 if (0 != SafeMaximumSize.u.HighPart)
843 {
844 return STATUS_INVALID_PARAMETER_3;
845 }
846 if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
847 {
848 return STATUS_INVALID_PARAMETER_MIX;
849 }
850
851 Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
852 PreviousMode,
853 FileName);
854 if (!NT_SUCCESS(Status))
855 {
856 return(Status);
857 }
858
859 InitializeObjectAttributes(&ObjectAttributes,
860 &CapturedFileName,
861 0,
862 NULL,
863 NULL);
864
865 Status = IoCreateFile(&FileHandle,
866 FILE_ALL_ACCESS,
867 &ObjectAttributes,
868 &IoStatus,
869 NULL,
870 0,
871 0,
872 FILE_OPEN_IF,
873 FILE_SYNCHRONOUS_IO_NONALERT,
874 NULL,
875 0,
876 CreateFileTypeNone,
877 NULL,
878 SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
879
880 ReleaseCapturedUnicodeString(&CapturedFileName,
881 PreviousMode);
882 if (!NT_SUCCESS(Status))
883 {
884 return(Status);
885 }
886
887 Status = ZwQueryVolumeInformationFile(FileHandle,
888 &IoStatus,
889 &FsSizeInformation,
890 sizeof(FILE_FS_SIZE_INFORMATION),
891 FileFsSizeInformation);
892 if (!NT_SUCCESS(Status))
893 {
894 ZwClose(FileHandle);
895 return Status;
896 }
897
898 BytesPerAllocationUnit = FsSizeInformation.SectorsPerAllocationUnit *
899 FsSizeInformation.BytesPerSector;
900
901 /* We have to find a value which is a multiple of both PAGE_SIZE and
902 BytesPerAllocationUnit */
903 SafeInitialSize.u.LowPart = ((SafeInitialSize.u.LowPart + PAGE_SIZE - 1) /
904 PAGE_SIZE) * PAGE_SIZE;
905 while (0 != (SafeInitialSize.u.LowPart % BytesPerAllocationUnit) &&
906 SafeInitialSize.u.LowPart <= SafeMaximumSize.u.LowPart - PAGE_SIZE)
907 {
908 SafeInitialSize.u.LowPart += PAGE_SIZE;
909 }
910 if (0 != (SafeInitialSize.u.LowPart % BytesPerAllocationUnit))
911 {
912 ZwClose(FileHandle);
913 return STATUS_ALLOTTED_SPACE_EXCEEDED;
914 }
915 ASSERT(0 == (SafeInitialSize.u.LowPart % PAGE_SIZE));
916
917 Status = ZwSetInformationFile(FileHandle,
918 &IoStatus,
919 &SafeInitialSize,
920 sizeof(LARGE_INTEGER),
921 FileAllocationInformation);
922 if (!NT_SUCCESS(Status))
923 {
924 ZwClose(FileHandle);
925 return(Status);
926 }
927
928 Status = ObReferenceObjectByHandle(FileHandle,
929 FILE_ALL_ACCESS,
930 IoFileObjectType,
931 PreviousMode,
932 (PVOID*)&FileObject,
933 NULL);
934 if (!NT_SUCCESS(Status))
935 {
936 ZwClose(FileHandle);
937 return(Status);
938 }
939
940 CurrentRetDescList = RetDescList = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
941
942 if (CurrentRetDescList == NULL)
943 {
944 ObDereferenceObject(FileObject);
945 ZwClose(FileHandle);
946 return(STATUS_NO_MEMORY);
947 }
948
949 #if defined(__GNUC__)
950 Vcn.QuadPart = 0LL;
951 #else
952
953 Vcn.QuadPart = 0;
954 #endif
955
956 ExtentCount = 0;
957 MaxVcn.QuadPart = (SafeInitialSize.QuadPart + BytesPerAllocationUnit - 1) / BytesPerAllocationUnit;
958 while(1)
959 {
960 Status = ZwFsControlFile(FileHandle,
961 0,
962 NULL,
963 NULL,
964 &IoStatus,
965 FSCTL_GET_RETRIEVAL_POINTERS,
966 &Vcn,
967 sizeof(LARGE_INTEGER),
968 &CurrentRetDescList->RetrievalPointers,
969 sizeof(RETRIEVAL_POINTERS_BUFFER) + PAIRS_PER_RUN * 2 * sizeof(LARGE_INTEGER));
970 if (!NT_SUCCESS(Status))
971 {
972 while (RetDescList)
973 {
974 CurrentRetDescList = RetDescList;
975 RetDescList = RetDescList->Next;
976 ExFreePool(CurrentRetDescList);
977 }
978 ObDereferenceObject(FileObject);
979 ZwClose(FileHandle);
980 return(Status);
981 }
982 ExtentCount += CurrentRetDescList->RetrievalPointers.ExtentCount;
983 if (CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn.QuadPart < MaxVcn.QuadPart)
984 {
985 CurrentRetDescList->Next = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
986 if (CurrentRetDescList->Next == NULL)
987 {
988 while (RetDescList)
989 {
990 CurrentRetDescList = RetDescList;
991 RetDescList = RetDescList->Next;
992 ExFreePool(CurrentRetDescList);
993 }
994 ObDereferenceObject(FileObject);
995 ZwClose(FileHandle);
996 return(STATUS_NO_MEMORY);
997 }
998 Vcn = CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn;
999 CurrentRetDescList = CurrentRetDescList->Next;
1000 }
1001 else
1002 {
1003 break;
1004 }
1005 }
1006
1007 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
1008 if (PagingFile == NULL)
1009 {
1010 while (RetDescList)
1011 {
1012 CurrentRetDescList = RetDescList;
1013 RetDescList = RetDescList->Next;
1014 ExFreePool(CurrentRetDescList);
1015 }
1016 ObDereferenceObject(FileObject);
1017 ZwClose(FileHandle);
1018 return(STATUS_NO_MEMORY);
1019 }
1020
1021 RtlZeroMemory(PagingFile, sizeof(*PagingFile));
1022
1023 PagingFile->FileObject = FileObject;
1024 PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
1025 PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
1026 PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
1027 PagingFile->UsedPages = 0;
1028 KeInitializeSpinLock(&PagingFile->AllocMapLock);
1029
1030 AllocMapSize = (PagingFile->FreePages / 32) + 1;
1031 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
1032 AllocMapSize * sizeof(ULONG));
1033 PagingFile->AllocMapSize = AllocMapSize;
1034
1035 if (PagingFile->AllocMap == NULL)
1036 {
1037 while (RetDescList)
1038 {
1039 CurrentRetDescList = RetDescList;
1040 RetDescList = RetDescList->Next;
1041 ExFreePool(CurrentRetDescList);
1042 }
1043 ExFreePool(PagingFile);
1044 ObDereferenceObject(FileObject);
1045 ZwClose(FileHandle);
1046 return(STATUS_NO_MEMORY);
1047 }
1048 DPRINT("ExtentCount: %d\n", ExtentCount);
1049 Size = sizeof(RETRIEVAL_POINTERS_BUFFER) + ExtentCount * 2 * sizeof(LARGE_INTEGER);
1050 PagingFile->RetrievalPointers = ExAllocatePool(NonPagedPool, Size);
1051 if (PagingFile->RetrievalPointers == NULL)
1052 {
1053 while (RetDescList)
1054 {
1055 CurrentRetDescList = RetDescList;
1056 RetDescList = RetDescList->Next;
1057 ExFreePool(CurrentRetDescList);
1058 }
1059 ExFreePool(PagingFile->AllocMap);
1060 ExFreePool(PagingFile);
1061 ObDereferenceObject(FileObject);
1062 ZwClose(FileHandle);
1063 return(STATUS_NO_MEMORY);
1064 }
1065
1066 RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG));
1067 RtlZeroMemory(PagingFile->RetrievalPointers, Size);
1068
1069 Count = 0;
1070 PagingFile->RetrievalPointers->ExtentCount = ExtentCount;
1071 PagingFile->RetrievalPointers->StartingVcn = RetDescList->RetrievalPointers.StartingVcn;
1072 CurrentRetDescList = RetDescList;
1073 while (CurrentRetDescList)
1074 {
1075 memcpy(&PagingFile->RetrievalPointers->Extents[Count],
1076 CurrentRetDescList->RetrievalPointers.Extents,
1077 CurrentRetDescList->RetrievalPointers.ExtentCount * 2 * sizeof(LARGE_INTEGER));
1078 Count += CurrentRetDescList->RetrievalPointers.ExtentCount;
1079 RetDescList = CurrentRetDescList;
1080 CurrentRetDescList = CurrentRetDescList->Next;
1081 ExFreePool(RetDescList);
1082 }
1083
1084 if (PagingFile->RetrievalPointers->ExtentCount != ExtentCount ||
1085 PagingFile->RetrievalPointers->Extents[ExtentCount - 1].NextVcn.QuadPart != MaxVcn.QuadPart)
1086 {
1087 ExFreePool(PagingFile->RetrievalPointers);
1088 ExFreePool(PagingFile->AllocMap);
1089 ExFreePool(PagingFile);
1090 ObDereferenceObject(FileObject);
1091 ZwClose(FileHandle);
1092 return(STATUS_UNSUCCESSFUL);
1093 }
1094
1095 /*
1096 * Change the entries from lcn's to volume offset's.
1097 */
1098 PagingFile->RetrievalPointers->StartingVcn.QuadPart *= BytesPerAllocationUnit;
1099 for (i = 0; i < ExtentCount; i++)
1100 {
1101 PagingFile->RetrievalPointers->Extents[i].Lcn.QuadPart *= BytesPerAllocationUnit;
1102 PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *= BytesPerAllocationUnit;
1103 }
1104
1105 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
1106 for (i = 0; i < MAX_PAGING_FILES; i++)
1107 {
1108 if (PagingFileList[i] == NULL)
1109 {
1110 PagingFileList[i] = PagingFile;
1111 break;
1112 }
1113 }
1114 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
1115 MiPagingFileCount++;
1116 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
1117
1118 /* Check whether this pagefile can be a crash dump target. */
1119 if (MmCoreDumpType != MM_CORE_DUMP_TYPE_NONE &&
1120 PagingFile->CurrentSize.QuadPart >= MmCoreDumpSize &&
1121 MmCoreDumpPageFile == 0xFFFFFFFF)
1122 {
1123 MmInitializeCrashDump(FileHandle, i);
1124 }
1125 ZwClose(FileHandle);
1126
1127 MmSwapSpaceMessage = FALSE;
1128
1129 return(STATUS_SUCCESS);
1130 }
1131
1132 /* EOF */