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