- Removed the process from the parameter list of MmCreateMemoryArea.
[reactos.git] / reactos / ntoskrnl / mm / section.c
1 /* $Id$
2 *
3 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 *
19 *
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/section.c
22 * PURPOSE: Implements section objects
23 *
24 * PROGRAMMERS: Rex Jolliff
25 * David Welch
26 * Eric Kohl
27 * Emanuele Aliberti
28 * Eugene Ingerman
29 * Hartmut Birr
30 * Casper Hornstrup
31 * KJK::Hyperion
32 * Guido de Jong
33 * Ge van Geldorp
34 * Royce Mitchell III
35 * Filip Navara
36 * Aleksey Bragin
37 * Jason Filby
38 * Thomas Weidenmueller
39 * Gunnar Andre' Dalsnes
40 * Mike Nordell
41 * Alex Ionescu
42 * Gregor Anich
43 * Steven Edwards
44 * Herve Poussineau
45 */
46
47 /* INCLUDES *****************************************************************/
48
49 #include <ntoskrnl.h>
50 #define NDEBUG
51 #include <internal/debug.h>
52
53 #include <reactos/exeformat.h>
54
55 /* TYPES *********************************************************************/
56
57 typedef struct
58 {
59 PSECTION_OBJECT Section;
60 PMM_SECTION_SEGMENT Segment;
61 ULONG Offset;
62 BOOLEAN WasDirty;
63 BOOLEAN Private;
64 }
65 MM_SECTION_PAGEOUT_CONTEXT;
66
67 /* GLOBALS *******************************************************************/
68
69 POBJECT_TYPE EXPORTED MmSectionObjectType = NULL;
70
71 static GENERIC_MAPPING MmpSectionMapping = {
72 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
73 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
74 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
75 SECTION_ALL_ACCESS};
76
77 #define PAGE_FROM_SSE(E) ((E) & 0xFFFFF000)
78 #define PFN_FROM_SSE(E) ((E) >> PAGE_SHIFT)
79 #define SHARE_COUNT_FROM_SSE(E) (((E) & 0x00000FFE) >> 1)
80 #define IS_SWAP_FROM_SSE(E) ((E) & 0x00000001)
81 #define MAX_SHARE_COUNT 0x7FF
82 #define MAKE_SSE(P, C) ((P) | ((C) << 1))
83 #define SWAPENTRY_FROM_SSE(E) ((E) >> 1)
84 #define MAKE_SWAP_SSE(S) (((S) << 1) | 0x1)
85
86 static const INFORMATION_CLASS_INFO ExSectionInfoClass[] =
87 {
88 ICI_SQ_SAME( sizeof(SECTION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionBasicInformation */
89 ICI_SQ_SAME( sizeof(SECTION_IMAGE_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionImageInformation */
90 };
91
92 /* FUNCTIONS *****************************************************************/
93
94 /* Note: Mmsp prefix denotes "Memory Manager Section Private". */
95
96 /*
97 * FUNCTION: Waits in kernel mode up to ten seconds for an MM_PAGEOP event.
98 * ARGUMENTS: PMM_PAGEOP which event we should wait for.
99 * RETURNS: Status of the wait.
100 */
101 static NTSTATUS
102 MmspWaitForPageOpCompletionEvent(PMM_PAGEOP PageOp)
103 {
104 LARGE_INTEGER Timeout;
105 #ifdef __GNUC__ /* TODO: Use other macro to check for suffix to use? */
106
107 Timeout.QuadPart = -100000000LL; // 10 sec
108 #else
109
110 Timeout.QuadPart = -100000000; // 10 sec
111 #endif
112
113 return KeWaitForSingleObject(&PageOp->CompletionEvent, 0, KernelMode, FALSE, &Timeout);
114 }
115
116
117 /*
118 * FUNCTION: Sets the page op completion event and releases the page op.
119 * ARGUMENTS: PMM_PAGEOP.
120 * RETURNS: In shorter time than it takes you to even read this
121 * description, so don't even think about geting a mug of coffee.
122 */
123 static void
124 MmspCompleteAndReleasePageOp(PMM_PAGEOP PageOp)
125 {
126 KeSetEvent(&PageOp->CompletionEvent, IO_NO_INCREMENT, FALSE);
127 MmReleasePageOp(PageOp);
128 }
129
130
131 /*
132 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
133 * ARGUMENTS: PFILE_OBJECT to wait for.
134 * RETURNS: Status of the wait.
135 */
136 static NTSTATUS
137 MmspWaitForFileLock(PFILE_OBJECT File)
138 {
139 return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
140 }
141
142
143 VOID
144 MmFreePageTablesSectionSegment(PMM_SECTION_SEGMENT Segment)
145 {
146 ULONG i;
147 if (Segment->Length > NR_SECTION_PAGE_TABLES * PAGE_SIZE)
148 {
149 for (i = 0; i < NR_SECTION_PAGE_TABLES; i++)
150 {
151 if (Segment->PageDirectory.PageTables[i] != NULL)
152 {
153 ExFreePool(Segment->PageDirectory.PageTables[i]);
154 }
155 }
156 }
157 }
158
159 VOID
160 NTAPI
161 MmFreeSectionSegments(PFILE_OBJECT FileObject)
162 {
163 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
164 {
165 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
166 PMM_SECTION_SEGMENT SectionSegments;
167 ULONG NrSegments;
168 ULONG i;
169
170 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
171 NrSegments = ImageSectionObject->NrSegments;
172 SectionSegments = ImageSectionObject->Segments;
173 for (i = 0; i < NrSegments; i++)
174 {
175 if (SectionSegments[i].ReferenceCount != 0)
176 {
177 DPRINT1("Image segment %d still referenced (was %d)\n", i,
178 SectionSegments[i].ReferenceCount);
179 KEBUGCHECK(0);
180 }
181 MmFreePageTablesSectionSegment(&SectionSegments[i]);
182 }
183 ExFreePool(ImageSectionObject->Segments);
184 ExFreePool(ImageSectionObject);
185 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
186 }
187 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
188 {
189 PMM_SECTION_SEGMENT Segment;
190
191 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
192 DataSectionObject;
193
194 if (Segment->ReferenceCount != 0)
195 {
196 DPRINT1("Data segment still referenced\n");
197 KEBUGCHECK(0);
198 }
199 MmFreePageTablesSectionSegment(Segment);
200 ExFreePool(Segment);
201 FileObject->SectionObjectPointer->DataSectionObject = NULL;
202 }
203 }
204
205 VOID
206 NTAPI
207 MmLockSectionSegment(PMM_SECTION_SEGMENT Segment)
208 {
209 ExAcquireFastMutex(&Segment->Lock);
210 }
211
212 VOID
213 NTAPI
214 MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment)
215 {
216 ExReleaseFastMutex(&Segment->Lock);
217 }
218
219 VOID
220 NTAPI
221 MmSetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
222 ULONG Offset,
223 ULONG Entry)
224 {
225 PSECTION_PAGE_TABLE Table;
226 ULONG DirectoryOffset;
227 ULONG TableOffset;
228
229 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
230 {
231 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
232 }
233 else
234 {
235 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
236 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
237 if (Table == NULL)
238 {
239 Table =
240 Segment->PageDirectory.PageTables[DirectoryOffset] =
241 ExAllocatePoolWithTag(PagedPool, sizeof(SECTION_PAGE_TABLE),
242 TAG_SECTION_PAGE_TABLE);
243 if (Table == NULL)
244 {
245 KEBUGCHECK(0);
246 }
247 memset(Table, 0, sizeof(SECTION_PAGE_TABLE));
248 DPRINT("Table %x\n", Table);
249 }
250 }
251 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
252 Table->Entry[TableOffset] = Entry;
253 }
254
255
256 ULONG
257 NTAPI
258 MmGetPageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
259 ULONG Offset)
260 {
261 PSECTION_PAGE_TABLE Table;
262 ULONG Entry;
263 ULONG DirectoryOffset;
264 ULONG TableOffset;
265
266 DPRINT("MmGetPageEntrySection(Offset %x)\n", Offset);
267
268 if (Segment->Length <= NR_SECTION_PAGE_TABLES * PAGE_SIZE)
269 {
270 Table = (PSECTION_PAGE_TABLE)&Segment->PageDirectory;
271 }
272 else
273 {
274 DirectoryOffset = PAGE_TO_SECTION_PAGE_DIRECTORY_OFFSET(Offset);
275 Table = Segment->PageDirectory.PageTables[DirectoryOffset];
276 DPRINT("Table %x\n", Table);
277 if (Table == NULL)
278 {
279 return(0);
280 }
281 }
282 TableOffset = PAGE_TO_SECTION_PAGE_TABLE_OFFSET(Offset);
283 Entry = Table->Entry[TableOffset];
284 return(Entry);
285 }
286
287 VOID
288 NTAPI
289 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
290 ULONG Offset)
291 {
292 ULONG Entry;
293
294 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
295 if (Entry == 0)
296 {
297 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
298 KEBUGCHECK(0);
299 }
300 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
301 {
302 DPRINT1("Maximum share count reached\n");
303 KEBUGCHECK(0);
304 }
305 if (IS_SWAP_FROM_SSE(Entry))
306 {
307 KEBUGCHECK(0);
308 }
309 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
310 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
311 }
312
313 BOOLEAN
314 NTAPI
315 MmUnsharePageEntrySectionSegment(PSECTION_OBJECT Section,
316 PMM_SECTION_SEGMENT Segment,
317 ULONG Offset,
318 BOOLEAN Dirty,
319 BOOLEAN PageOut)
320 {
321 ULONG Entry;
322 BOOLEAN IsDirectMapped = FALSE;
323
324 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
325 if (Entry == 0)
326 {
327 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
328 KEBUGCHECK(0);
329 }
330 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
331 {
332 DPRINT1("Zero share count for unshare\n");
333 KEBUGCHECK(0);
334 }
335 if (IS_SWAP_FROM_SSE(Entry))
336 {
337 KEBUGCHECK(0);
338 }
339 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
340 /*
341 * If we reducing the share count of this entry to zero then set the entry
342 * to zero and tell the cache the page is no longer mapped.
343 */
344 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
345 {
346 PFILE_OBJECT FileObject;
347 PBCB Bcb;
348 SWAPENTRY SavedSwapEntry;
349 PFN_TYPE Page;
350 BOOLEAN IsImageSection;
351 ULONG FileOffset;
352
353 FileOffset = Offset + Segment->FileOffset;
354
355 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
356
357 Page = PFN_FROM_SSE(Entry);
358 FileObject = Section->FileObject;
359 if (FileObject != NULL &&
360 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
361 {
362
363 if ((FileOffset % PAGE_SIZE) == 0 &&
364 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
365 {
366 NTSTATUS Status;
367 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
368 IsDirectMapped = TRUE;
369 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, Dirty);
370 if (!NT_SUCCESS(Status))
371 {
372 DPRINT1("CcRosUnmapCacheSegment failed, status = %x\n", Status);
373 KEBUGCHECK(0);
374 }
375 }
376 }
377
378 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
379 if (SavedSwapEntry == 0)
380 {
381 if (!PageOut &&
382 ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
383 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED)))
384 {
385 /*
386 * FIXME:
387 * Try to page out this page and set the swap entry
388 * within the section segment. There exist no rmap entry
389 * for this page. The pager thread can't page out a
390 * page without a rmap entry.
391 */
392 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
393 }
394 else
395 {
396 MmSetPageEntrySectionSegment(Segment, Offset, 0);
397 if (!IsDirectMapped)
398 {
399 MmReleasePageMemoryConsumer(MC_USER, Page);
400 }
401 }
402 }
403 else
404 {
405 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
406 (Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
407 {
408 if (!PageOut)
409 {
410 if (Dirty)
411 {
412 /*
413 * FIXME:
414 * We hold all locks. Nobody can do something with the current
415 * process and the current segment (also not within an other process).
416 */
417 NTSTATUS Status;
418 Status = MmWriteToSwapPage(SavedSwapEntry, Page);
419 if (!NT_SUCCESS(Status))
420 {
421 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status);
422 KEBUGCHECK(0);
423 }
424 }
425 MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
426 MmSetSavedSwapEntryPage(Page, 0);
427 }
428 MmReleasePageMemoryConsumer(MC_USER, Page);
429 }
430 else
431 {
432 DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n");
433 KEBUGCHECK(0);
434 }
435 }
436 }
437 else
438 {
439 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
440 }
441 return(SHARE_COUNT_FROM_SSE(Entry) > 0);
442 }
443
444 BOOL MiIsPageFromCache(PMEMORY_AREA MemoryArea,
445 ULONG SegOffset)
446 {
447 if (!(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
448 {
449 PBCB Bcb;
450 PCACHE_SEGMENT CacheSeg;
451 Bcb = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
452 CacheSeg = CcRosLookupCacheSegment(Bcb, SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset);
453 if (CacheSeg)
454 {
455 CcRosReleaseCacheSegment(Bcb, CacheSeg, CacheSeg->Valid, FALSE, TRUE);
456 return TRUE;
457 }
458 }
459 return FALSE;
460 }
461
462 NTSTATUS
463 NTAPI
464 MiReadPage(PMEMORY_AREA MemoryArea,
465 ULONG SegOffset,
466 PPFN_TYPE Page)
467 /*
468 * FUNCTION: Read a page for a section backed memory area.
469 * PARAMETERS:
470 * MemoryArea - Memory area to read the page for.
471 * Offset - Offset of the page to read.
472 * Page - Variable that receives a page contains the read data.
473 */
474 {
475 ULONG BaseOffset;
476 ULONG FileOffset;
477 PVOID BaseAddress;
478 BOOLEAN UptoDate;
479 PCACHE_SEGMENT CacheSeg;
480 PFILE_OBJECT FileObject;
481 NTSTATUS Status;
482 ULONG RawLength;
483 PBCB Bcb;
484 BOOLEAN IsImageSection;
485 ULONG Length;
486
487 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
488 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
489 RawLength = MemoryArea->Data.SectionData.Segment->RawLength;
490 FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->FileOffset;
491 IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
492
493 ASSERT(Bcb);
494
495 DPRINT("%S %x\n", FileObject->FileName.Buffer, FileOffset);
496
497 /*
498 * If the file system is letting us go directly to the cache and the
499 * memory area was mapped at an offset in the file which is page aligned
500 * then get the related cache segment.
501 */
502 if ((FileOffset % PAGE_SIZE) == 0 &&
503 (SegOffset + PAGE_SIZE <= RawLength || !IsImageSection) &&
504 !(MemoryArea->Data.SectionData.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
505 {
506
507 /*
508 * Get the related cache segment; we use a lower level interface than
509 * filesystems do because it is safe for us to use an offset with a
510 * alignment less than the file system block size.
511 */
512 Status = CcRosGetCacheSegment(Bcb,
513 FileOffset,
514 &BaseOffset,
515 &BaseAddress,
516 &UptoDate,
517 &CacheSeg);
518 if (!NT_SUCCESS(Status))
519 {
520 return(Status);
521 }
522 if (!UptoDate)
523 {
524 /*
525 * If the cache segment isn't up to date then call the file
526 * system to read in the data.
527 */
528 Status = ReadCacheSegment(CacheSeg);
529 if (!NT_SUCCESS(Status))
530 {
531 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
532 return Status;
533 }
534 }
535 /*
536 * Retrieve the page from the cache segment that we actually want.
537 */
538 (*Page) = MmGetPhysicalAddress((char*)BaseAddress +
539 FileOffset - BaseOffset).QuadPart >> PAGE_SHIFT;
540
541 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, TRUE);
542 }
543 else
544 {
545 PVOID PageAddr;
546 ULONG CacheSegOffset;
547 /*
548 * Allocate a page, this is rather complicated by the possibility
549 * we might have to move other things out of memory
550 */
551 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
552 if (!NT_SUCCESS(Status))
553 {
554 return(Status);
555 }
556 Status = CcRosGetCacheSegment(Bcb,
557 FileOffset,
558 &BaseOffset,
559 &BaseAddress,
560 &UptoDate,
561 &CacheSeg);
562 if (!NT_SUCCESS(Status))
563 {
564 return(Status);
565 }
566 if (!UptoDate)
567 {
568 /*
569 * If the cache segment isn't up to date then call the file
570 * system to read in the data.
571 */
572 Status = ReadCacheSegment(CacheSeg);
573 if (!NT_SUCCESS(Status))
574 {
575 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
576 return Status;
577 }
578 }
579 PageAddr = MmCreateHyperspaceMapping(*Page);
580 CacheSegOffset = BaseOffset + CacheSeg->Bcb->CacheSegmentSize - FileOffset;
581 Length = RawLength - SegOffset;
582 if (Length <= CacheSegOffset && Length <= PAGE_SIZE)
583 {
584 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
585 }
586 else if (CacheSegOffset >= PAGE_SIZE)
587 {
588 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
589 }
590 else
591 {
592 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, CacheSegOffset);
593 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
594 Status = CcRosGetCacheSegment(Bcb,
595 FileOffset + CacheSegOffset,
596 &BaseOffset,
597 &BaseAddress,
598 &UptoDate,
599 &CacheSeg);
600 if (!NT_SUCCESS(Status))
601 {
602 MmDeleteHyperspaceMapping(PageAddr);
603 return(Status);
604 }
605 if (!UptoDate)
606 {
607 /*
608 * If the cache segment isn't up to date then call the file
609 * system to read in the data.
610 */
611 Status = ReadCacheSegment(CacheSeg);
612 if (!NT_SUCCESS(Status))
613 {
614 CcRosReleaseCacheSegment(Bcb, CacheSeg, FALSE, FALSE, FALSE);
615 MmDeleteHyperspaceMapping(PageAddr);
616 return Status;
617 }
618 }
619 if (Length < PAGE_SIZE)
620 {
621 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, Length - CacheSegOffset);
622 }
623 else
624 {
625 memcpy((char*)PageAddr + CacheSegOffset, BaseAddress, PAGE_SIZE - CacheSegOffset);
626 }
627 }
628 CcRosReleaseCacheSegment(Bcb, CacheSeg, TRUE, FALSE, FALSE);
629 MmDeleteHyperspaceMapping(PageAddr);
630 }
631 return(STATUS_SUCCESS);
632 }
633
634 NTSTATUS
635 NTAPI
636 MmNotPresentFaultSectionView(PMADDRESS_SPACE AddressSpace,
637 MEMORY_AREA* MemoryArea,
638 PVOID Address,
639 BOOLEAN Locked)
640 {
641 ULONG Offset;
642 PFN_TYPE Page;
643 NTSTATUS Status;
644 PVOID PAddress;
645 PSECTION_OBJECT Section;
646 PMM_SECTION_SEGMENT Segment;
647 ULONG Entry;
648 ULONG Entry1;
649 ULONG Attributes;
650 PMM_PAGEOP PageOp;
651 PMM_REGION Region;
652 BOOL HasSwapEntry;
653
654 /*
655 * There is a window between taking the page fault and locking the
656 * address space when another thread could load the page so we check
657 * that.
658 */
659 if (MmIsPagePresent(AddressSpace->Process, Address))
660 {
661 if (Locked)
662 {
663 MmLockPage(MmGetPfnForProcess(AddressSpace->Process, Address));
664 }
665 return(STATUS_SUCCESS);
666 }
667
668 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
669 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
670 + MemoryArea->Data.SectionData.ViewOffset;
671
672 Segment = MemoryArea->Data.SectionData.Segment;
673 Section = MemoryArea->Data.SectionData.Section;
674 Region = MmFindRegion(MemoryArea->StartingAddress,
675 &MemoryArea->Data.SectionData.RegionListHead,
676 Address, NULL);
677 /*
678 * Lock the segment
679 */
680 MmLockSectionSegment(Segment);
681
682 /*
683 * Check if this page needs to be mapped COW
684 */
685 if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
686 (Region->Protect == PAGE_READWRITE ||
687 Region->Protect == PAGE_EXECUTE_READWRITE))
688 {
689 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
690 }
691 else
692 {
693 Attributes = Region->Protect;
694 }
695
696 /*
697 * Get or create a page operation descriptor
698 */
699 PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset, MM_PAGEOP_PAGEIN, FALSE);
700 if (PageOp == NULL)
701 {
702 DPRINT1("MmGetPageOp failed\n");
703 KEBUGCHECK(0);
704 }
705
706 /*
707 * Check if someone else is already handling this fault, if so wait
708 * for them
709 */
710 if (PageOp->Thread != PsGetCurrentThread())
711 {
712 MmUnlockSectionSegment(Segment);
713 MmUnlockAddressSpace(AddressSpace);
714 Status = MmspWaitForPageOpCompletionEvent(PageOp);
715 /*
716 * Check for various strange conditions
717 */
718 if (Status != STATUS_SUCCESS)
719 {
720 DPRINT1("Failed to wait for page op, status = %x\n", Status);
721 KEBUGCHECK(0);
722 }
723 if (PageOp->Status == STATUS_PENDING)
724 {
725 DPRINT1("Woke for page op before completion\n");
726 KEBUGCHECK(0);
727 }
728 MmLockAddressSpace(AddressSpace);
729 /*
730 * If this wasn't a pagein then restart the operation
731 */
732 if (PageOp->OpType != MM_PAGEOP_PAGEIN)
733 {
734 MmspCompleteAndReleasePageOp(PageOp);
735 DPRINT("Address 0x%.8X\n", Address);
736 return(STATUS_MM_RESTART_OPERATION);
737 }
738
739 /*
740 * If the thread handling this fault has failed then we don't retry
741 */
742 if (!NT_SUCCESS(PageOp->Status))
743 {
744 Status = PageOp->Status;
745 MmspCompleteAndReleasePageOp(PageOp);
746 DPRINT("Address 0x%.8X\n", Address);
747 return(Status);
748 }
749 MmLockSectionSegment(Segment);
750 /*
751 * If the completed fault was for another address space then set the
752 * page in this one.
753 */
754 if (!MmIsPagePresent(AddressSpace->Process, Address))
755 {
756 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
757 HasSwapEntry = MmIsPageSwapEntry(AddressSpace->Process, (PVOID)PAddress);
758
759 if (PAGE_FROM_SSE(Entry) == 0 || HasSwapEntry)
760 {
761 /*
762 * The page was a private page in another or in our address space
763 */
764 MmUnlockSectionSegment(Segment);
765 MmspCompleteAndReleasePageOp(PageOp);
766 return(STATUS_MM_RESTART_OPERATION);
767 }
768
769 Page = PFN_FROM_SSE(Entry);
770
771 MmSharePageEntrySectionSegment(Segment, Offset);
772
773 /* FIXME: Should we call MmCreateVirtualMappingUnsafe if
774 * (Section->AllocationAttributes & SEC_PHYSICALMEMORY) is true?
775 */
776 Status = MmCreateVirtualMapping(AddressSpace->Process,
777 Address,
778 Attributes,
779 &Page,
780 1);
781 if (!NT_SUCCESS(Status))
782 {
783 DbgPrint("Unable to create virtual mapping\n");
784 KEBUGCHECK(0);
785 }
786 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
787 }
788 if (Locked)
789 {
790 MmLockPage(Page);
791 }
792 MmUnlockSectionSegment(Segment);
793 PageOp->Status = STATUS_SUCCESS;
794 MmspCompleteAndReleasePageOp(PageOp);
795 DPRINT("Address 0x%.8X\n", Address);
796 return(STATUS_SUCCESS);
797 }
798
799 HasSwapEntry = MmIsPageSwapEntry(AddressSpace->Process, (PVOID)PAddress);
800 if (HasSwapEntry)
801 {
802 /*
803 * Must be private page we have swapped out.
804 */
805 SWAPENTRY SwapEntry;
806
807 /*
808 * Sanity check
809 */
810 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
811 {
812 DPRINT1("Found a swaped out private page in a pagefile section.\n");
813 KEBUGCHECK(0);
814 }
815
816 MmUnlockSectionSegment(Segment);
817 MmDeletePageFileMapping(AddressSpace->Process, (PVOID)PAddress, &SwapEntry);
818
819 MmUnlockAddressSpace(AddressSpace);
820 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
821 if (!NT_SUCCESS(Status))
822 {
823 KEBUGCHECK(0);
824 }
825
826 Status = MmReadFromSwapPage(SwapEntry, Page);
827 if (!NT_SUCCESS(Status))
828 {
829 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
830 KEBUGCHECK(0);
831 }
832 MmLockAddressSpace(AddressSpace);
833 Status = MmCreateVirtualMapping(AddressSpace->Process,
834 Address,
835 Region->Protect,
836 &Page,
837 1);
838 if (!NT_SUCCESS(Status))
839 {
840 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
841 KEBUGCHECK(0);
842 return(Status);
843 }
844
845 /*
846 * Store the swap entry for later use.
847 */
848 MmSetSavedSwapEntryPage(Page, SwapEntry);
849
850 /*
851 * Add the page to the process's working set
852 */
853 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
854
855 /*
856 * Finish the operation
857 */
858 if (Locked)
859 {
860 MmLockPage(Page);
861 }
862 PageOp->Status = STATUS_SUCCESS;
863 MmspCompleteAndReleasePageOp(PageOp);
864 DPRINT("Address 0x%.8X\n", Address);
865 return(STATUS_SUCCESS);
866 }
867
868 /*
869 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
870 */
871 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
872 {
873 MmUnlockSectionSegment(Segment);
874 /*
875 * Just map the desired physical page
876 */
877 Page = Offset >> PAGE_SHIFT;
878 Status = MmCreateVirtualMappingUnsafe(AddressSpace->Process,
879 Address,
880 Region->Protect,
881 &Page,
882 1);
883 if (!NT_SUCCESS(Status))
884 {
885 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
886 KEBUGCHECK(0);
887 return(Status);
888 }
889 /*
890 * Don't add an rmap entry since the page mapped could be for
891 * anything.
892 */
893 if (Locked)
894 {
895 MmLockPageUnsafe(Page);
896 }
897
898 /*
899 * Cleanup and release locks
900 */
901 PageOp->Status = STATUS_SUCCESS;
902 MmspCompleteAndReleasePageOp(PageOp);
903 DPRINT("Address 0x%.8X\n", Address);
904 return(STATUS_SUCCESS);
905 }
906
907 /*
908 * Map anonymous memory for BSS sections
909 */
910 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
911 {
912 MmUnlockSectionSegment(Segment);
913 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
914 if (!NT_SUCCESS(Status))
915 {
916 MmUnlockAddressSpace(AddressSpace);
917 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
918 MmLockAddressSpace(AddressSpace);
919 }
920 if (!NT_SUCCESS(Status))
921 {
922 KEBUGCHECK(0);
923 }
924 Status = MmCreateVirtualMapping(AddressSpace->Process,
925 Address,
926 Region->Protect,
927 &Page,
928 1);
929 if (!NT_SUCCESS(Status))
930 {
931 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
932 KEBUGCHECK(0);
933 return(Status);
934 }
935 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
936 if (Locked)
937 {
938 MmLockPage(Page);
939 }
940
941 /*
942 * Cleanup and release locks
943 */
944 PageOp->Status = STATUS_SUCCESS;
945 MmspCompleteAndReleasePageOp(PageOp);
946 DPRINT("Address 0x%.8X\n", Address);
947 return(STATUS_SUCCESS);
948 }
949
950 /*
951 * Get the entry corresponding to the offset within the section
952 */
953 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
954
955 if (Entry == 0)
956 {
957 /*
958 * If the entry is zero (and it can't change because we have
959 * locked the segment) then we need to load the page.
960 */
961
962 /*
963 * Release all our locks and read in the page from disk
964 */
965 MmUnlockSectionSegment(Segment);
966 MmUnlockAddressSpace(AddressSpace);
967
968 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
969 (Offset >= PAGE_ROUND_UP(Segment->RawLength) && Section->AllocationAttributes & SEC_IMAGE))
970 {
971 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
972 if (!NT_SUCCESS(Status))
973 {
974 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
975 }
976 }
977 else
978 {
979 Status = MiReadPage(MemoryArea, Offset, &Page);
980 if (!NT_SUCCESS(Status))
981 {
982 DPRINT1("MiReadPage failed (Status %x)\n", Status);
983 }
984 }
985 if (!NT_SUCCESS(Status))
986 {
987 /*
988 * FIXME: What do we know in this case?
989 */
990 /*
991 * Cleanup and release locks
992 */
993 MmLockAddressSpace(AddressSpace);
994 PageOp->Status = Status;
995 MmspCompleteAndReleasePageOp(PageOp);
996 DPRINT("Address 0x%.8X\n", Address);
997 return(Status);
998 }
999 /*
1000 * Relock the address space and segment
1001 */
1002 MmLockAddressSpace(AddressSpace);
1003 MmLockSectionSegment(Segment);
1004
1005 /*
1006 * Check the entry. No one should change the status of a page
1007 * that has a pending page-in.
1008 */
1009 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1010 if (Entry != Entry1)
1011 {
1012 DbgPrint("Someone changed ppte entry while we slept\n");
1013 KEBUGCHECK(0);
1014 }
1015
1016 /*
1017 * Mark the offset within the section as having valid, in-memory
1018 * data
1019 */
1020 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1021 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1022 MmUnlockSectionSegment(Segment);
1023
1024 Status = MmCreateVirtualMapping(AddressSpace->Process,
1025 Address,
1026 Attributes,
1027 &Page,
1028 1);
1029 if (!NT_SUCCESS(Status))
1030 {
1031 DbgPrint("Unable to create virtual mapping\n");
1032 KEBUGCHECK(0);
1033 }
1034 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1035
1036 if (Locked)
1037 {
1038 MmLockPage(Page);
1039 }
1040 PageOp->Status = STATUS_SUCCESS;
1041 MmspCompleteAndReleasePageOp(PageOp);
1042 DPRINT("Address 0x%.8X\n", Address);
1043 return(STATUS_SUCCESS);
1044 }
1045 else if (IS_SWAP_FROM_SSE(Entry))
1046 {
1047 SWAPENTRY SwapEntry;
1048
1049 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1050
1051 /*
1052 * Release all our locks and read in the page from disk
1053 */
1054 MmUnlockSectionSegment(Segment);
1055
1056 MmUnlockAddressSpace(AddressSpace);
1057
1058 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1059 if (!NT_SUCCESS(Status))
1060 {
1061 KEBUGCHECK(0);
1062 }
1063
1064 Status = MmReadFromSwapPage(SwapEntry, Page);
1065 if (!NT_SUCCESS(Status))
1066 {
1067 KEBUGCHECK(0);
1068 }
1069
1070 /*
1071 * Relock the address space and segment
1072 */
1073 MmLockAddressSpace(AddressSpace);
1074 MmLockSectionSegment(Segment);
1075
1076 /*
1077 * Check the entry. No one should change the status of a page
1078 * that has a pending page-in.
1079 */
1080 Entry1 = MmGetPageEntrySectionSegment(Segment, Offset);
1081 if (Entry != Entry1)
1082 {
1083 DbgPrint("Someone changed ppte entry while we slept\n");
1084 KEBUGCHECK(0);
1085 }
1086
1087 /*
1088 * Mark the offset within the section as having valid, in-memory
1089 * data
1090 */
1091 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1092 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1093 MmUnlockSectionSegment(Segment);
1094
1095 /*
1096 * Save the swap entry.
1097 */
1098 MmSetSavedSwapEntryPage(Page, SwapEntry);
1099 Status = MmCreateVirtualMapping(AddressSpace->Process,
1100 Address,
1101 Region->Protect,
1102 &Page,
1103 1);
1104 if (!NT_SUCCESS(Status))
1105 {
1106 DbgPrint("Unable to create virtual mapping\n");
1107 KEBUGCHECK(0);
1108 }
1109 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1110 if (Locked)
1111 {
1112 MmLockPage(Page);
1113 }
1114 PageOp->Status = STATUS_SUCCESS;
1115 MmspCompleteAndReleasePageOp(PageOp);
1116 DPRINT("Address 0x%.8X\n", Address);
1117 return(STATUS_SUCCESS);
1118 }
1119 else
1120 {
1121 /*
1122 * If the section offset is already in-memory and valid then just
1123 * take another reference to the page
1124 */
1125
1126 Page = PFN_FROM_SSE(Entry);
1127
1128 MmSharePageEntrySectionSegment(Segment, Offset);
1129 MmUnlockSectionSegment(Segment);
1130
1131 Status = MmCreateVirtualMapping(AddressSpace->Process,
1132 Address,
1133 Attributes,
1134 &Page,
1135 1);
1136 if (!NT_SUCCESS(Status))
1137 {
1138 DbgPrint("Unable to create virtual mapping\n");
1139 KEBUGCHECK(0);
1140 }
1141 MmInsertRmap(Page, AddressSpace->Process, (PVOID)PAddress);
1142 if (Locked)
1143 {
1144 MmLockPage(Page);
1145 }
1146 PageOp->Status = STATUS_SUCCESS;
1147 MmspCompleteAndReleasePageOp(PageOp);
1148 DPRINT("Address 0x%.8X\n", Address);
1149 return(STATUS_SUCCESS);
1150 }
1151 }
1152
1153 NTSTATUS
1154 NTAPI
1155 MmAccessFaultSectionView(PMADDRESS_SPACE AddressSpace,
1156 MEMORY_AREA* MemoryArea,
1157 PVOID Address,
1158 BOOLEAN Locked)
1159 {
1160 PMM_SECTION_SEGMENT Segment;
1161 PSECTION_OBJECT Section;
1162 PFN_TYPE OldPage;
1163 PFN_TYPE NewPage;
1164 NTSTATUS Status;
1165 PVOID PAddress;
1166 ULONG Offset;
1167 PMM_PAGEOP PageOp;
1168 PMM_REGION Region;
1169 ULONG Entry;
1170
1171 /*
1172 * Check if the page has been paged out or has already been set readwrite
1173 */
1174 if (!MmIsPagePresent(AddressSpace->Process, Address) ||
1175 MmGetPageProtect(AddressSpace->Process, Address) & PAGE_READWRITE)
1176 {
1177 DPRINT("Address 0x%.8X\n", Address);
1178 return(STATUS_SUCCESS);
1179 }
1180
1181 /*
1182 * Find the offset of the page
1183 */
1184 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1185 Offset = (ULONG_PTR)PAddress - (ULONG_PTR)MemoryArea->StartingAddress
1186 + MemoryArea->Data.SectionData.ViewOffset;
1187
1188 Segment = MemoryArea->Data.SectionData.Segment;
1189 Section = MemoryArea->Data.SectionData.Section;
1190 Region = MmFindRegion(MemoryArea->StartingAddress,
1191 &MemoryArea->Data.SectionData.RegionListHead,
1192 Address, NULL);
1193 /*
1194 * Lock the segment
1195 */
1196 MmLockSectionSegment(Segment);
1197
1198 OldPage = MmGetPfnForProcess(NULL, Address);
1199 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1200
1201 MmUnlockSectionSegment(Segment);
1202
1203 /*
1204 * Check if we are doing COW
1205 */
1206 if (!((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
1207 (Region->Protect == PAGE_READWRITE ||
1208 Region->Protect == PAGE_EXECUTE_READWRITE)))
1209 {
1210 DPRINT("Address 0x%.8X\n", Address);
1211 return(STATUS_UNSUCCESSFUL);
1212 }
1213
1214 if (IS_SWAP_FROM_SSE(Entry) ||
1215 PFN_FROM_SSE(Entry) != OldPage)
1216 {
1217 /* This is a private page. We must only change the page protection. */
1218 MmSetPageProtect(AddressSpace->Process, PAddress, Region->Protect);
1219 return(STATUS_SUCCESS);
1220 }
1221
1222 /*
1223 * Get or create a pageop
1224 */
1225 PageOp = MmGetPageOp(MemoryArea, NULL, 0, Segment, Offset,
1226 MM_PAGEOP_ACCESSFAULT, FALSE);
1227 if (PageOp == NULL)
1228 {
1229 DPRINT1("MmGetPageOp failed\n");
1230 KEBUGCHECK(0);
1231 }
1232
1233 /*
1234 * Wait for any other operations to complete
1235 */
1236 if (PageOp->Thread != PsGetCurrentThread())
1237 {
1238 MmUnlockAddressSpace(AddressSpace);
1239 Status = MmspWaitForPageOpCompletionEvent(PageOp);
1240 /*
1241 * Check for various strange conditions
1242 */
1243 if (Status == STATUS_TIMEOUT)
1244 {
1245 DPRINT1("Failed to wait for page op, status = %x\n", Status);
1246 KEBUGCHECK(0);
1247 }
1248 if (PageOp->Status == STATUS_PENDING)
1249 {
1250 DPRINT1("Woke for page op before completion\n");
1251 KEBUGCHECK(0);
1252 }
1253 /*
1254 * Restart the operation
1255 */
1256 MmLockAddressSpace(AddressSpace);
1257 MmspCompleteAndReleasePageOp(PageOp);
1258 DPRINT("Address 0x%.8X\n", Address);
1259 return(STATUS_MM_RESTART_OPERATION);
1260 }
1261
1262 /*
1263 * Release locks now we have the pageop
1264 */
1265 MmUnlockAddressSpace(AddressSpace);
1266
1267 /*
1268 * Allocate a page
1269 */
1270 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1271 if (!NT_SUCCESS(Status))
1272 {
1273 KEBUGCHECK(0);
1274 }
1275
1276 /*
1277 * Copy the old page
1278 */
1279 MiCopyFromUserPage(NewPage, PAddress);
1280
1281 MmLockAddressSpace(AddressSpace);
1282 /*
1283 * Delete the old entry.
1284 */
1285 MmDeleteVirtualMapping(AddressSpace->Process, Address, FALSE, NULL, NULL);
1286
1287 /*
1288 * Set the PTE to point to the new page
1289 */
1290 Status = MmCreateVirtualMapping(AddressSpace->Process,
1291 Address,
1292 Region->Protect,
1293 &NewPage,
1294 1);
1295 if (!NT_SUCCESS(Status))
1296 {
1297 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1298 KEBUGCHECK(0);
1299 return(Status);
1300 }
1301 if (!NT_SUCCESS(Status))
1302 {
1303 DbgPrint("Unable to create virtual mapping\n");
1304 KEBUGCHECK(0);
1305 }
1306 if (Locked)
1307 {
1308 MmLockPage(NewPage);
1309 MmUnlockPage(OldPage);
1310 }
1311
1312 /*
1313 * Unshare the old page.
1314 */
1315 MmDeleteRmap(OldPage, AddressSpace->Process, PAddress);
1316 MmInsertRmap(NewPage, AddressSpace->Process, PAddress);
1317 MmLockSectionSegment(Segment);
1318 MmUnsharePageEntrySectionSegment(Section, Segment, Offset, FALSE, FALSE);
1319 MmUnlockSectionSegment(Segment);
1320
1321 PageOp->Status = STATUS_SUCCESS;
1322 MmspCompleteAndReleasePageOp(PageOp);
1323 DPRINT("Address 0x%.8X\n", Address);
1324 return(STATUS_SUCCESS);
1325 }
1326
1327 VOID
1328 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1329 {
1330 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1331 BOOL WasDirty;
1332 PFN_TYPE Page;
1333
1334 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1335 if (Process)
1336 {
1337 MmLockAddressSpace(&Process->AddressSpace);
1338 }
1339
1340 MmDeleteVirtualMapping(Process,
1341 Address,
1342 FALSE,
1343 &WasDirty,
1344 &Page);
1345 if (WasDirty)
1346 {
1347 PageOutContext->WasDirty = TRUE;
1348 }
1349 if (!PageOutContext->Private)
1350 {
1351 MmLockSectionSegment(PageOutContext->Segment);
1352 MmUnsharePageEntrySectionSegment(PageOutContext->Section,
1353 PageOutContext->Segment,
1354 PageOutContext->Offset,
1355 PageOutContext->WasDirty,
1356 TRUE);
1357 MmUnlockSectionSegment(PageOutContext->Segment);
1358 }
1359 if (Process)
1360 {
1361 MmUnlockAddressSpace(&Process->AddressSpace);
1362 }
1363
1364 if (PageOutContext->Private)
1365 {
1366 MmReleasePageMemoryConsumer(MC_USER, Page);
1367 }
1368
1369 DPRINT("PhysicalAddress %x, Address %x\n", Page << PAGE_SHIFT, Address);
1370 }
1371
1372 NTSTATUS
1373 NTAPI
1374 MmPageOutSectionView(PMADDRESS_SPACE AddressSpace,
1375 MEMORY_AREA* MemoryArea,
1376 PVOID Address,
1377 PMM_PAGEOP PageOp)
1378 {
1379 PFN_TYPE Page;
1380 MM_SECTION_PAGEOUT_CONTEXT Context;
1381 SWAPENTRY SwapEntry;
1382 ULONG Entry;
1383 ULONG FileOffset;
1384 NTSTATUS Status;
1385 PFILE_OBJECT FileObject;
1386 PBCB Bcb = NULL;
1387 BOOLEAN DirectMapped;
1388 BOOLEAN IsImageSection;
1389
1390 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1391
1392 /*
1393 * Get the segment and section.
1394 */
1395 Context.Segment = MemoryArea->Data.SectionData.Segment;
1396 Context.Section = MemoryArea->Data.SectionData.Section;
1397
1398 Context.Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
1399 + MemoryArea->Data.SectionData.ViewOffset;
1400 FileOffset = Context.Offset + Context.Segment->FileOffset;
1401
1402 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1403
1404 FileObject = Context.Section->FileObject;
1405 DirectMapped = FALSE;
1406 if (FileObject != NULL &&
1407 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1408 {
1409 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1410
1411 /*
1412 * If the file system is letting us go directly to the cache and the
1413 * memory area was mapped at an offset in the file which is page aligned
1414 * then note this is a direct mapped page.
1415 */
1416 if ((FileOffset % PAGE_SIZE) == 0 &&
1417 (Context.Offset + PAGE_SIZE <= Context.Segment->RawLength || !IsImageSection))
1418 {
1419 DirectMapped = TRUE;
1420 }
1421 }
1422
1423
1424 /*
1425 * This should never happen since mappings of physical memory are never
1426 * placed in the rmap lists.
1427 */
1428 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1429 {
1430 DPRINT1("Trying to page out from physical memory section address 0x%X "
1431 "process %d\n", Address,
1432 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0);
1433 KEBUGCHECK(0);
1434 }
1435
1436 /*
1437 * Get the section segment entry and the physical address.
1438 */
1439 Entry = MmGetPageEntrySectionSegment(Context.Segment, Context.Offset);
1440 if (!MmIsPagePresent(AddressSpace->Process, Address))
1441 {
1442 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1443 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0, Address);
1444 KEBUGCHECK(0);
1445 }
1446 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1447 SwapEntry = MmGetSavedSwapEntryPage(Page);
1448
1449 /*
1450 * Prepare the context structure for the rmap delete call.
1451 */
1452 Context.WasDirty = FALSE;
1453 if (Context.Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1454 IS_SWAP_FROM_SSE(Entry) ||
1455 PFN_FROM_SSE(Entry) != Page)
1456 {
1457 Context.Private = TRUE;
1458 }
1459 else
1460 {
1461 Context.Private = FALSE;
1462 }
1463
1464 /*
1465 * Take an additional reference to the page or the cache segment.
1466 */
1467 if (DirectMapped && !Context.Private)
1468 {
1469 if(!MiIsPageFromCache(MemoryArea, Context.Offset))
1470 {
1471 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
1472 KEBUGCHECK(0);
1473 }
1474 }
1475 else
1476 {
1477 MmReferencePage(Page);
1478 }
1479
1480 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
1481
1482 /*
1483 * If this wasn't a private page then we should have reduced the entry to
1484 * zero by deleting all the rmaps.
1485 */
1486 if (!Context.Private && MmGetPageEntrySectionSegment(Context.Segment, Context.Offset) != 0)
1487 {
1488 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
1489 !(Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1490 {
1491 KEBUGCHECK(0);
1492 }
1493 }
1494
1495 /*
1496 * If the page wasn't dirty then we can just free it as for a readonly page.
1497 * Since we unmapped all the mappings above we know it will not suddenly
1498 * become dirty.
1499 * If the page is from a pagefile section and has no swap entry,
1500 * we can't free the page at this point.
1501 */
1502 SwapEntry = MmGetSavedSwapEntryPage(Page);
1503 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
1504 {
1505 if (Context.Private)
1506 {
1507 DPRINT1("Found a %s private page (address %x) in a pagefile segment.\n",
1508 Context.WasDirty ? "dirty" : "clean", Address);
1509 KEBUGCHECK(0);
1510 }
1511 if (!Context.WasDirty && SwapEntry != 0)
1512 {
1513 MmSetSavedSwapEntryPage(Page, 0);
1514 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1515 MmReleasePageMemoryConsumer(MC_USER, Page);
1516 PageOp->Status = STATUS_SUCCESS;
1517 MmspCompleteAndReleasePageOp(PageOp);
1518 return(STATUS_SUCCESS);
1519 }
1520 }
1521 else if (Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
1522 {
1523 if (Context.Private)
1524 {
1525 DPRINT1("Found a %s private page (address %x) in a shared section segment.\n",
1526 Context.WasDirty ? "dirty" : "clean", Address);
1527 KEBUGCHECK(0);
1528 }
1529 if (!Context.WasDirty || SwapEntry != 0)
1530 {
1531 MmSetSavedSwapEntryPage(Page, 0);
1532 if (SwapEntry != 0)
1533 {
1534 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1535 }
1536 MmReleasePageMemoryConsumer(MC_USER, Page);
1537 PageOp->Status = STATUS_SUCCESS;
1538 MmspCompleteAndReleasePageOp(PageOp);
1539 return(STATUS_SUCCESS);
1540 }
1541 }
1542 else if (!Context.Private && DirectMapped)
1543 {
1544 if (SwapEntry != 0)
1545 {
1546 DPRINT1("Found a swapentry for a non private and direct mapped page (address %x)\n",
1547 Address);
1548 KEBUGCHECK(0);
1549 }
1550 Status = CcRosUnmapCacheSegment(Bcb, FileOffset, FALSE);
1551 if (!NT_SUCCESS(Status))
1552 {
1553 DPRINT1("CCRosUnmapCacheSegment failed, status = %x\n", Status);
1554 KEBUGCHECK(0);
1555 }
1556 PageOp->Status = STATUS_SUCCESS;
1557 MmspCompleteAndReleasePageOp(PageOp);
1558 return(STATUS_SUCCESS);
1559 }
1560 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
1561 {
1562 if (SwapEntry != 0)
1563 {
1564 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %x)\n",
1565 Address);
1566 KEBUGCHECK(0);
1567 }
1568 MmReleasePageMemoryConsumer(MC_USER, Page);
1569 PageOp->Status = STATUS_SUCCESS;
1570 MmspCompleteAndReleasePageOp(PageOp);
1571 return(STATUS_SUCCESS);
1572 }
1573 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
1574 {
1575 MmSetSavedSwapEntryPage(Page, 0);
1576 MmLockAddressSpace(AddressSpace);
1577 Status = MmCreatePageFileMapping(AddressSpace->Process,
1578 Address,
1579 SwapEntry);
1580 MmUnlockAddressSpace(AddressSpace);
1581 if (!NT_SUCCESS(Status))
1582 {
1583 KEBUGCHECK(0);
1584 }
1585 MmReleasePageMemoryConsumer(MC_USER, Page);
1586 PageOp->Status = STATUS_SUCCESS;
1587 MmspCompleteAndReleasePageOp(PageOp);
1588 return(STATUS_SUCCESS);
1589 }
1590
1591 /*
1592 * If necessary, allocate an entry in the paging file for this page
1593 */
1594 if (SwapEntry == 0)
1595 {
1596 SwapEntry = MmAllocSwapPage();
1597 if (SwapEntry == 0)
1598 {
1599 MmShowOutOfSpaceMessagePagingFile();
1600 MmLockAddressSpace(AddressSpace);
1601 /*
1602 * For private pages restore the old mappings.
1603 */
1604 if (Context.Private)
1605 {
1606 Status = MmCreateVirtualMapping(AddressSpace->Process,
1607 Address,
1608 MemoryArea->Protect,
1609 &Page,
1610 1);
1611 MmSetDirtyPage(AddressSpace->Process, Address);
1612 MmInsertRmap(Page,
1613 AddressSpace->Process,
1614 Address);
1615 }
1616 else
1617 {
1618 /*
1619 * For non-private pages if the page wasn't direct mapped then
1620 * set it back into the section segment entry so we don't loose
1621 * our copy. Otherwise it will be handled by the cache manager.
1622 */
1623 Status = MmCreateVirtualMapping(AddressSpace->Process,
1624 Address,
1625 MemoryArea->Protect,
1626 &Page,
1627 1);
1628 MmSetDirtyPage(AddressSpace->Process, Address);
1629 MmInsertRmap(Page,
1630 AddressSpace->Process,
1631 Address);
1632 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1633 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1634 }
1635 MmUnlockAddressSpace(AddressSpace);
1636 PageOp->Status = STATUS_UNSUCCESSFUL;
1637 MmspCompleteAndReleasePageOp(PageOp);
1638 return(STATUS_PAGEFILE_QUOTA);
1639 }
1640 }
1641
1642 /*
1643 * Write the page to the pagefile
1644 */
1645 Status = MmWriteToSwapPage(SwapEntry, Page);
1646 if (!NT_SUCCESS(Status))
1647 {
1648 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1649 Status);
1650 /*
1651 * As above: undo our actions.
1652 * FIXME: Also free the swap page.
1653 */
1654 MmLockAddressSpace(AddressSpace);
1655 if (Context.Private)
1656 {
1657 Status = MmCreateVirtualMapping(AddressSpace->Process,
1658 Address,
1659 MemoryArea->Protect,
1660 &Page,
1661 1);
1662 MmSetDirtyPage(AddressSpace->Process, Address);
1663 MmInsertRmap(Page,
1664 AddressSpace->Process,
1665 Address);
1666 }
1667 else
1668 {
1669 Status = MmCreateVirtualMapping(AddressSpace->Process,
1670 Address,
1671 MemoryArea->Protect,
1672 &Page,
1673 1);
1674 MmSetDirtyPage(AddressSpace->Process, Address);
1675 MmInsertRmap(Page,
1676 AddressSpace->Process,
1677 Address);
1678 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1679 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1680 }
1681 MmUnlockAddressSpace(AddressSpace);
1682 PageOp->Status = STATUS_UNSUCCESSFUL;
1683 MmspCompleteAndReleasePageOp(PageOp);
1684 return(STATUS_UNSUCCESSFUL);
1685 }
1686
1687 /*
1688 * Otherwise we have succeeded.
1689 */
1690 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
1691 MmSetSavedSwapEntryPage(Page, 0);
1692 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
1693 Context.Segment->Characteristics & IMAGE_SCN_MEM_SHARED)
1694 {
1695 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, MAKE_SWAP_SSE(SwapEntry));
1696 }
1697 else
1698 {
1699 MmReleasePageMemoryConsumer(MC_USER, Page);
1700 }
1701
1702 if (Context.Private)
1703 {
1704 MmLockAddressSpace(AddressSpace);
1705 Status = MmCreatePageFileMapping(AddressSpace->Process,
1706 Address,
1707 SwapEntry);
1708 MmUnlockAddressSpace(AddressSpace);
1709 if (!NT_SUCCESS(Status))
1710 {
1711 KEBUGCHECK(0);
1712 }
1713 }
1714 else
1715 {
1716 Entry = MAKE_SWAP_SSE(SwapEntry);
1717 MmSetPageEntrySectionSegment(Context.Segment, Context.Offset, Entry);
1718 }
1719
1720 PageOp->Status = STATUS_SUCCESS;
1721 MmspCompleteAndReleasePageOp(PageOp);
1722 return(STATUS_SUCCESS);
1723 }
1724
1725 NTSTATUS
1726 NTAPI
1727 MmWritePageSectionView(PMADDRESS_SPACE AddressSpace,
1728 PMEMORY_AREA MemoryArea,
1729 PVOID Address,
1730 PMM_PAGEOP PageOp)
1731 {
1732 ULONG Offset;
1733 PSECTION_OBJECT Section;
1734 PMM_SECTION_SEGMENT Segment;
1735 PFN_TYPE Page;
1736 SWAPENTRY SwapEntry;
1737 ULONG Entry;
1738 BOOLEAN Private;
1739 NTSTATUS Status;
1740 PFILE_OBJECT FileObject;
1741 PBCB Bcb = NULL;
1742 BOOLEAN DirectMapped;
1743 BOOLEAN IsImageSection;
1744
1745 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1746
1747 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
1748 + MemoryArea->Data.SectionData.ViewOffset;
1749
1750 /*
1751 * Get the segment and section.
1752 */
1753 Segment = MemoryArea->Data.SectionData.Segment;
1754 Section = MemoryArea->Data.SectionData.Section;
1755 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1756
1757 FileObject = Section->FileObject;
1758 DirectMapped = FALSE;
1759 if (FileObject != NULL &&
1760 !(Segment->Characteristics & IMAGE_SCN_MEM_SHARED))
1761 {
1762 Bcb = FileObject->SectionObjectPointer->SharedCacheMap;
1763
1764 /*
1765 * If the file system is letting us go directly to the cache and the
1766 * memory area was mapped at an offset in the file which is page aligned
1767 * then note this is a direct mapped page.
1768 */
1769 if (((Offset + Segment->FileOffset) % PAGE_SIZE) == 0 &&
1770 (Offset + PAGE_SIZE <= Segment->RawLength || !IsImageSection))
1771 {
1772 DirectMapped = TRUE;
1773 }
1774 }
1775
1776 /*
1777 * This should never happen since mappings of physical memory are never
1778 * placed in the rmap lists.
1779 */
1780 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1781 {
1782 DPRINT1("Trying to write back page from physical memory mapped at %X "
1783 "process %d\n", Address,
1784 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0);
1785 KEBUGCHECK(0);
1786 }
1787
1788 /*
1789 * Get the section segment entry and the physical address.
1790 */
1791 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1792 if (!MmIsPagePresent(AddressSpace->Process, Address))
1793 {
1794 DPRINT1("Trying to page out not-present page at (%d,0x%.8X).\n",
1795 AddressSpace->Process ? AddressSpace->Process->UniqueProcessId : 0, Address);
1796 KEBUGCHECK(0);
1797 }
1798 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1799 SwapEntry = MmGetSavedSwapEntryPage(Page);
1800
1801 /*
1802 * Check for a private (COWed) page.
1803 */
1804 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1805 IS_SWAP_FROM_SSE(Entry) ||
1806 PFN_FROM_SSE(Entry) != Page)
1807 {
1808 Private = TRUE;
1809 }
1810 else
1811 {
1812 Private = FALSE;
1813 }
1814
1815 /*
1816 * Speculatively set all mappings of the page to clean.
1817 */
1818 MmSetCleanAllRmaps(Page);
1819
1820 /*
1821 * If this page was direct mapped from the cache then the cache manager
1822 * will take care of writing it back to disk.
1823 */
1824 if (DirectMapped && !Private)
1825 {
1826 ASSERT(SwapEntry == 0);
1827 CcRosMarkDirtyCacheSegment(Bcb, Offset + Segment->FileOffset);
1828 PageOp->Status = STATUS_SUCCESS;
1829 MmspCompleteAndReleasePageOp(PageOp);
1830 return(STATUS_SUCCESS);
1831 }
1832
1833 /*
1834 * If necessary, allocate an entry in the paging file for this page
1835 */
1836 if (SwapEntry == 0)
1837 {
1838 SwapEntry = MmAllocSwapPage();
1839 if (SwapEntry == 0)
1840 {
1841 MmSetDirtyAllRmaps(Page);
1842 PageOp->Status = STATUS_UNSUCCESSFUL;
1843 MmspCompleteAndReleasePageOp(PageOp);
1844 return(STATUS_PAGEFILE_QUOTA);
1845 }
1846 MmSetSavedSwapEntryPage(Page, SwapEntry);
1847 }
1848
1849 /*
1850 * Write the page to the pagefile
1851 */
1852 Status = MmWriteToSwapPage(SwapEntry, Page);
1853 if (!NT_SUCCESS(Status))
1854 {
1855 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
1856 Status);
1857 MmSetDirtyAllRmaps(Page);
1858 PageOp->Status = STATUS_UNSUCCESSFUL;
1859 MmspCompleteAndReleasePageOp(PageOp);
1860 return(STATUS_UNSUCCESSFUL);
1861 }
1862
1863 /*
1864 * Otherwise we have succeeded.
1865 */
1866 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
1867 PageOp->Status = STATUS_SUCCESS;
1868 MmspCompleteAndReleasePageOp(PageOp);
1869 return(STATUS_SUCCESS);
1870 }
1871
1872 VOID STATIC
1873 MmAlterViewAttributes(PMADDRESS_SPACE AddressSpace,
1874 PVOID BaseAddress,
1875 ULONG RegionSize,
1876 ULONG OldType,
1877 ULONG OldProtect,
1878 ULONG NewType,
1879 ULONG NewProtect)
1880 {
1881 PMEMORY_AREA MemoryArea;
1882 PMM_SECTION_SEGMENT Segment;
1883 BOOL DoCOW = FALSE;
1884 ULONG i;
1885
1886 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1887 Segment = MemoryArea->Data.SectionData.Segment;
1888
1889 if ((Segment->WriteCopy || MemoryArea->Data.SectionData.WriteCopyView) &&
1890 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1891 {
1892 DoCOW = TRUE;
1893 }
1894
1895 if (OldProtect != NewProtect)
1896 {
1897 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1898 {
1899 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1900 ULONG Protect = NewProtect;
1901
1902 /*
1903 * If we doing COW for this segment then check if the page is
1904 * already private.
1905 */
1906 if (DoCOW && MmIsPagePresent(AddressSpace->Process, Address))
1907 {
1908 ULONG Offset;
1909 ULONG Entry;
1910 PFN_TYPE Page;
1911
1912 Offset = (ULONG_PTR)Address - (ULONG_PTR)MemoryArea->StartingAddress
1913 + MemoryArea->Data.SectionData.ViewOffset;
1914 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1915 Page = MmGetPfnForProcess(AddressSpace->Process, Address);
1916
1917 Protect = PAGE_READONLY;
1918 if (Segment->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1919 IS_SWAP_FROM_SSE(Entry) ||
1920 PFN_FROM_SSE(Entry) != Page)
1921 {
1922 Protect = NewProtect;
1923 }
1924 }
1925
1926 if (MmIsPagePresent(AddressSpace->Process, Address))
1927 {
1928 MmSetPageProtect(AddressSpace->Process, Address,
1929 Protect);
1930 }
1931 }
1932 }
1933 }
1934
1935 NTSTATUS
1936 NTAPI
1937 MmProtectSectionView(PMADDRESS_SPACE AddressSpace,
1938 PMEMORY_AREA MemoryArea,
1939 PVOID BaseAddress,
1940 ULONG Length,
1941 ULONG Protect,
1942 PULONG OldProtect)
1943 {
1944 PMM_REGION Region;
1945 NTSTATUS Status;
1946 ULONG_PTR MaxLength;
1947
1948 MaxLength = (ULONG_PTR)MemoryArea->EndingAddress - (ULONG_PTR)BaseAddress;
1949 if (Length > MaxLength)
1950 Length = MaxLength;
1951
1952 Region = MmFindRegion(MemoryArea->StartingAddress,
1953 &MemoryArea->Data.SectionData.RegionListHead,
1954 BaseAddress, NULL);
1955 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
1956 Region->Protect != Protect)
1957 {
1958 CHECKPOINT1;
1959 return STATUS_INVALID_PAGE_PROTECTION;
1960 }
1961
1962 *OldProtect = Region->Protect;
1963 Status = MmAlterRegion(AddressSpace, MemoryArea->StartingAddress,
1964 &MemoryArea->Data.SectionData.RegionListHead,
1965 BaseAddress, Length, Region->Type, Protect,
1966 MmAlterViewAttributes);
1967
1968 return(Status);
1969 }
1970
1971 NTSTATUS STDCALL
1972 MmQuerySectionView(PMEMORY_AREA MemoryArea,
1973 PVOID Address,
1974 PMEMORY_BASIC_INFORMATION Info,
1975 PULONG ResultLength)
1976 {
1977 PMM_REGION Region;
1978 PVOID RegionBaseAddress;
1979 PSECTION_OBJECT Section;
1980 PMM_SECTION_SEGMENT Segment;
1981
1982 Region = MmFindRegion((PVOID)MemoryArea->StartingAddress,
1983 &MemoryArea->Data.SectionData.RegionListHead,
1984 Address, &RegionBaseAddress);
1985 if (Region == NULL)
1986 {
1987 return STATUS_UNSUCCESSFUL;
1988 }
1989
1990 Section = MemoryArea->Data.SectionData.Section;
1991 if (Section->AllocationAttributes & SEC_IMAGE)
1992 {
1993 Segment = MemoryArea->Data.SectionData.Segment;
1994 Info->AllocationBase = (PBYTE)MemoryArea->StartingAddress - Segment->VirtualAddress;
1995 Info->Type = MEM_IMAGE;
1996 }
1997 else
1998 {
1999 Info->AllocationBase = MemoryArea->StartingAddress;
2000 Info->Type = MEM_MAPPED;
2001 }
2002 Info->BaseAddress = RegionBaseAddress;
2003 Info->AllocationProtect = MemoryArea->Protect;
2004 Info->RegionSize = Region->Length;
2005 Info->State = MEM_COMMIT;
2006 Info->Protect = Region->Protect;
2007
2008 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2009 return(STATUS_SUCCESS);
2010 }
2011
2012 VOID
2013 NTAPI
2014 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2015 {
2016 ULONG Length;
2017 ULONG Offset;
2018 ULONG Entry;
2019 ULONG SavedSwapEntry;
2020 PFN_TYPE Page;
2021
2022 Page = 0;
2023
2024 Length = PAGE_ROUND_UP(Segment->Length);
2025 for (Offset = 0; Offset < Length; Offset += PAGE_SIZE)
2026 {
2027 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
2028 if (Entry)
2029 {
2030 if (IS_SWAP_FROM_SSE(Entry))
2031 {
2032 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2033 }
2034 else
2035 {
2036 Page = PFN_FROM_SSE(Entry);
2037 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2038 if (SavedSwapEntry != 0)
2039 {
2040 MmSetSavedSwapEntryPage(Page, 0);
2041 MmFreeSwapPage(SavedSwapEntry);
2042 }
2043 MmReleasePageMemoryConsumer(MC_USER, Page);
2044 }
2045 MmSetPageEntrySectionSegment(Segment, Offset, 0);
2046 }
2047 }
2048 }
2049
2050 VOID STDCALL
2051 MmpDeleteSection(PVOID ObjectBody)
2052 {
2053 PSECTION_OBJECT Section = (PSECTION_OBJECT)ObjectBody;
2054
2055 DPRINT("MmpDeleteSection(ObjectBody %x)\n", ObjectBody);
2056 if (Section->AllocationAttributes & SEC_IMAGE)
2057 {
2058 ULONG i;
2059 ULONG NrSegments;
2060 ULONG RefCount;
2061 PMM_SECTION_SEGMENT SectionSegments;
2062
2063 /*
2064 * NOTE: Section->ImageSection can be NULL for short time
2065 * during the section creating. If we fail for some reason
2066 * until the image section is properly initialized we shouldn't
2067 * process further here.
2068 */
2069 if (Section->ImageSection == NULL)
2070 return;
2071
2072 SectionSegments = Section->ImageSection->Segments;
2073 NrSegments = Section->ImageSection->NrSegments;
2074
2075 for (i = 0; i < NrSegments; i++)
2076 {
2077 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2078 {
2079 MmLockSectionSegment(&SectionSegments[i]);
2080 }
2081 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2082 if (SectionSegments[i].Characteristics & IMAGE_SCN_MEM_SHARED)
2083 {
2084 if (RefCount == 0)
2085 {
2086 MmpFreePageFileSegment(&SectionSegments[i]);
2087 }
2088 MmUnlockSectionSegment(&SectionSegments[i]);
2089 }
2090 }
2091 }
2092 else
2093 {
2094 /*
2095 * NOTE: Section->Segment can be NULL for short time
2096 * during the section creating.
2097 */
2098 if (Section->Segment == NULL)
2099 return;
2100
2101 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2102 {
2103 MmpFreePageFileSegment(Section->Segment);
2104 MmFreePageTablesSectionSegment(Section->Segment);
2105 ExFreePool(Section->Segment);
2106 Section->Segment = NULL;
2107 }
2108 else
2109 {
2110 InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2111 }
2112 }
2113 if (Section->FileObject != NULL)
2114 {
2115 CcRosDereferenceCache(Section->FileObject);
2116 ObDereferenceObject(Section->FileObject);
2117 Section->FileObject = NULL;
2118 }
2119 }
2120
2121 VOID STDCALL
2122 MmpCloseSection(PVOID ObjectBody,
2123 ULONG HandleCount)
2124 {
2125 DPRINT("MmpCloseSection(OB %x, HC %d) RC %d\n",
2126 ObjectBody, HandleCount, ObGetObjectPointerCount(ObjectBody));
2127 }
2128
2129 NTSTATUS
2130 INIT_FUNCTION
2131 NTAPI
2132 MmCreatePhysicalMemorySection(VOID)
2133 {
2134 PSECTION_OBJECT PhysSection;
2135 NTSTATUS Status;
2136 OBJECT_ATTRIBUTES Obj;
2137 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2138 LARGE_INTEGER SectionSize;
2139
2140 /*
2141 * Create the section mapping physical memory
2142 */
2143 SectionSize.QuadPart = 0xFFFFFFFF;
2144 InitializeObjectAttributes(&Obj,
2145 &Name,
2146 OBJ_PERMANENT,
2147 NULL,
2148 NULL);
2149 Status = MmCreateSection(&PhysSection,
2150 SECTION_ALL_ACCESS,
2151 &Obj,
2152 &SectionSize,
2153 PAGE_EXECUTE_READWRITE,
2154 0,
2155 NULL,
2156 NULL);
2157 if (!NT_SUCCESS(Status))
2158 {
2159 DbgPrint("Failed to create PhysicalMemory section\n");
2160 KEBUGCHECK(0);
2161 }
2162 Status = ObInsertObject(PhysSection,
2163 NULL,
2164 SECTION_ALL_ACCESS,
2165 0,
2166 NULL,
2167 NULL);
2168 if (!NT_SUCCESS(Status))
2169 {
2170 ObDereferenceObject(PhysSection);
2171 }
2172 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2173 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2174
2175 return(STATUS_SUCCESS);
2176 }
2177
2178 NTSTATUS
2179 INIT_FUNCTION
2180 NTAPI
2181 MmInitSectionImplementation(VOID)
2182 {
2183 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2184 UNICODE_STRING Name;
2185
2186 DPRINT("Creating Section Object Type\n");
2187
2188 /* Initialize the Section object type */
2189 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2190 RtlInitUnicodeString(&Name, L"Section");
2191 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2192 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION_OBJECT);
2193 ObjectTypeInitializer.PoolType = PagedPool;
2194 ObjectTypeInitializer.UseDefaultObject = TRUE;
2195 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2196 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2197 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2198 ObpCreateTypeObject(&ObjectTypeInitializer, &Name, &MmSectionObjectType);
2199
2200 return(STATUS_SUCCESS);
2201 }
2202
2203 NTSTATUS
2204 NTAPI
2205 MmCreatePageFileSection(PSECTION_OBJECT *SectionObject,
2206 ACCESS_MASK DesiredAccess,
2207 POBJECT_ATTRIBUTES ObjectAttributes,
2208 PLARGE_INTEGER UMaximumSize,
2209 ULONG SectionPageProtection,
2210 ULONG AllocationAttributes)
2211 /*
2212 * Create a section which is backed by the pagefile
2213 */
2214 {
2215 LARGE_INTEGER MaximumSize;
2216 PSECTION_OBJECT Section;
2217 PMM_SECTION_SEGMENT Segment;
2218 NTSTATUS Status;
2219
2220 if (UMaximumSize == NULL)
2221 {
2222 return(STATUS_UNSUCCESSFUL);
2223 }
2224 MaximumSize = *UMaximumSize;
2225
2226 /*
2227 * Create the section
2228 */
2229 Status = ObCreateObject(ExGetPreviousMode(),
2230 MmSectionObjectType,
2231 ObjectAttributes,
2232 ExGetPreviousMode(),
2233 NULL,
2234 sizeof(SECTION_OBJECT),
2235 0,
2236 0,
2237 (PVOID*)(PVOID)&Section);
2238 if (!NT_SUCCESS(Status))
2239 {
2240 return(Status);
2241 }
2242
2243 /*
2244 * Initialize it
2245 */
2246 Section->SectionPageProtection = SectionPageProtection;
2247 Section->AllocationAttributes = AllocationAttributes;
2248 Section->Segment = NULL;
2249 Section->FileObject = NULL;
2250 Section->MaximumSize = MaximumSize;
2251 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2252 TAG_MM_SECTION_SEGMENT);
2253 if (Segment == NULL)
2254 {
2255 ObDereferenceObject(Section);
2256 return(STATUS_NO_MEMORY);
2257 }
2258 Section->Segment = Segment;
2259 Segment->ReferenceCount = 1;
2260 ExInitializeFastMutex(&Segment->Lock);
2261 Segment->FileOffset = 0;
2262 Segment->Protection = SectionPageProtection;
2263 Segment->RawLength = MaximumSize.u.LowPart;
2264 Segment->Length = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2265 Segment->Flags = MM_PAGEFILE_SEGMENT;
2266 Segment->WriteCopy = FALSE;
2267 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2268 Segment->VirtualAddress = 0;
2269 Segment->Characteristics = 0;
2270 *SectionObject = Section;
2271 return(STATUS_SUCCESS);
2272 }
2273
2274
2275 NTSTATUS
2276 NTAPI
2277 MmCreateDataFileSection(PSECTION_OBJECT *SectionObject,
2278 ACCESS_MASK DesiredAccess,
2279 POBJECT_ATTRIBUTES ObjectAttributes,
2280 PLARGE_INTEGER UMaximumSize,
2281 ULONG SectionPageProtection,
2282 ULONG AllocationAttributes,
2283 HANDLE FileHandle)
2284 /*
2285 * Create a section backed by a data file
2286 */
2287 {
2288 PSECTION_OBJECT Section;
2289 NTSTATUS Status;
2290 LARGE_INTEGER MaximumSize;
2291 PFILE_OBJECT FileObject;
2292 PMM_SECTION_SEGMENT Segment;
2293 ULONG FileAccess;
2294 IO_STATUS_BLOCK Iosb;
2295 LARGE_INTEGER Offset;
2296 CHAR Buffer;
2297 FILE_STANDARD_INFORMATION FileInfo;
2298
2299 /*
2300 * Create the section
2301 */
2302 Status = ObCreateObject(ExGetPreviousMode(),
2303 MmSectionObjectType,
2304 ObjectAttributes,
2305 ExGetPreviousMode(),
2306 NULL,
2307 sizeof(SECTION_OBJECT),
2308 0,
2309 0,
2310 (PVOID*)(PVOID)&Section);
2311 if (!NT_SUCCESS(Status))
2312 {
2313 return(Status);
2314 }
2315 /*
2316 * Initialize it
2317 */
2318 Section->SectionPageProtection = SectionPageProtection;
2319 Section->AllocationAttributes = AllocationAttributes;
2320 Section->Segment = NULL;
2321
2322 /*
2323 * Check file access required
2324 */
2325 if (SectionPageProtection & PAGE_READWRITE ||
2326 SectionPageProtection & PAGE_EXECUTE_READWRITE)
2327 {
2328 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
2329 }
2330 else
2331 {
2332 FileAccess = FILE_READ_DATA;
2333 }
2334
2335 /*
2336 * Reference the file handle
2337 */
2338 Status = ObReferenceObjectByHandle(FileHandle,
2339 FileAccess,
2340 IoFileObjectType,
2341 UserMode,
2342 (PVOID*)(PVOID)&FileObject,
2343 NULL);
2344 if (!NT_SUCCESS(Status))
2345 {
2346 ObDereferenceObject(Section);
2347 return(Status);
2348 }
2349
2350 /*
2351 * FIXME: This is propably not entirely correct. We can't look into
2352 * the standard FCB header because it might not be initialized yet
2353 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2354 * standard file information is filled on first request).
2355 */
2356 Status = IoQueryFileInformation(FileObject,
2357 FileStandardInformation,
2358 sizeof(FILE_STANDARD_INFORMATION),
2359 &FileInfo,
2360 &Iosb.Information);
2361 if (!NT_SUCCESS(Status))
2362 {
2363 ObDereferenceObject(Section);
2364 ObDereferenceObject(FileObject);
2365 return Status;
2366 }
2367
2368 /*
2369 * FIXME: Revise this once a locking order for file size changes is
2370 * decided
2371 */
2372 if (UMaximumSize != NULL)
2373 {
2374 MaximumSize = *UMaximumSize;
2375 }
2376 else
2377 {
2378 MaximumSize = FileInfo.EndOfFile;
2379 /* Mapping zero-sized files isn't allowed. */
2380 if (MaximumSize.QuadPart == 0)
2381 {
2382 ObDereferenceObject(Section);
2383 ObDereferenceObject(FileObject);
2384 return STATUS_FILE_INVALID;
2385 }
2386 }
2387
2388 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
2389 {
2390 Status = IoSetInformation(FileObject,
2391 FileAllocationInformation,
2392 sizeof(LARGE_INTEGER),
2393 &MaximumSize);
2394 if (!NT_SUCCESS(Status))
2395 {
2396 ObDereferenceObject(Section);
2397 ObDereferenceObject(FileObject);
2398 return(STATUS_SECTION_NOT_EXTENDED);
2399 }
2400 }
2401
2402 if (FileObject->SectionObjectPointer == NULL ||
2403 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2404 {
2405 /*
2406 * Read a bit so caching is initiated for the file object.
2407 * This is only needed because MiReadPage currently cannot
2408 * handle non-cached streams.
2409 */
2410 Offset.QuadPart = 0;
2411 Status = ZwReadFile(FileHandle,
2412 NULL,
2413 NULL,
2414 NULL,
2415 &Iosb,
2416 &Buffer,
2417 sizeof (Buffer),
2418 &Offset,
2419 0);
2420 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
2421 {
2422 ObDereferenceObject(Section);
2423 ObDereferenceObject(FileObject);
2424 return(Status);
2425 }
2426 if (FileObject->SectionObjectPointer == NULL ||
2427 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
2428 {
2429 /* FIXME: handle this situation */
2430 ObDereferenceObject(Section);
2431 ObDereferenceObject(FileObject);
2432 return STATUS_INVALID_PARAMETER;
2433 }
2434 }
2435
2436 /*
2437 * Lock the file
2438 */
2439 Status = MmspWaitForFileLock(FileObject);
2440 if (Status != STATUS_SUCCESS)
2441 {
2442 ObDereferenceObject(Section);
2443 ObDereferenceObject(FileObject);
2444 return(Status);
2445 }
2446
2447 /*
2448 * If this file hasn't been mapped as a data file before then allocate a
2449 * section segment to describe the data file mapping
2450 */
2451 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
2452 {
2453 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2454 TAG_MM_SECTION_SEGMENT);
2455 if (Segment == NULL)
2456 {
2457 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2458 ObDereferenceObject(Section);
2459 ObDereferenceObject(FileObject);
2460 return(STATUS_NO_MEMORY);
2461 }
2462 Section->Segment = Segment;
2463 Segment->ReferenceCount = 1;
2464 ExInitializeFastMutex(&Segment->Lock);
2465 /*
2466 * Set the lock before assigning the segment to the file object
2467 */
2468 ExAcquireFastMutex(&Segment->Lock);
2469 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
2470
2471 Segment->FileOffset = 0;
2472 Segment->Protection = SectionPageProtection;
2473 Segment->Flags = MM_DATAFILE_SEGMENT;
2474 Segment->Characteristics = 0;
2475 Segment->WriteCopy = FALSE;
2476 if (AllocationAttributes & SEC_RESERVE)
2477 {
2478 Segment->Length = Segment->RawLength = 0;
2479 }
2480 else
2481 {
2482 Segment->RawLength = MaximumSize.u.LowPart;
2483 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2484 }
2485 Segment->VirtualAddress = 0;
2486 RtlZeroMemory(&Segment->PageDirectory, sizeof(SECTION_PAGE_DIRECTORY));
2487 }
2488 else
2489 {
2490 /*
2491 * If the file is already mapped as a data file then we may need
2492 * to extend it
2493 */
2494 Segment =
2495 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
2496 DataSectionObject;
2497 Section->Segment = Segment;
2498 InterlockedIncrementUL(&Segment->ReferenceCount);
2499 MmLockSectionSegment(Segment);
2500
2501 if (MaximumSize.u.LowPart > Segment->RawLength &&
2502 !(AllocationAttributes & SEC_RESERVE))
2503 {
2504 Segment->RawLength = MaximumSize.u.LowPart;
2505 Segment->Length = PAGE_ROUND_UP(Segment->RawLength);
2506 }
2507 }
2508 MmUnlockSectionSegment(Segment);
2509 Section->FileObject = FileObject;
2510 Section->MaximumSize = MaximumSize;
2511 CcRosReferenceCache(FileObject);
2512 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2513 *SectionObject = Section;
2514 return(STATUS_SUCCESS);
2515 }
2516
2517 /*
2518 TODO: not that great (declaring loaders statically, having to declare all of
2519 them, having to keep them extern, etc.), will fix in the future
2520 */
2521 extern NTSTATUS NTAPI PeFmtCreateSection
2522 (
2523 IN CONST VOID * FileHeader,
2524 IN SIZE_T FileHeaderSize,
2525 IN PVOID File,
2526 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2527 OUT PULONG Flags,
2528 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2529 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2530 );
2531
2532 extern NTSTATUS NTAPI ElfFmtCreateSection
2533 (
2534 IN CONST VOID * FileHeader,
2535 IN SIZE_T FileHeaderSize,
2536 IN PVOID File,
2537 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2538 OUT PULONG Flags,
2539 IN PEXEFMT_CB_READ_FILE ReadFileCb,
2540 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2541 );
2542
2543 /* TODO: this is a standard DDK/PSDK macro */
2544 #ifndef RTL_NUMBER_OF
2545 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
2546 #endif
2547
2548 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2549 {
2550 PeFmtCreateSection,
2551 ElfFmtCreateSection
2552 };
2553
2554 static
2555 PMM_SECTION_SEGMENT
2556 NTAPI
2557 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2558 {
2559 SIZE_T SizeOfSegments;
2560 PMM_SECTION_SEGMENT Segments;
2561
2562 /* TODO: check for integer overflow */
2563 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2564
2565 Segments = ExAllocatePoolWithTag(NonPagedPool,
2566 SizeOfSegments,
2567 TAG_MM_SECTION_SEGMENT);
2568
2569 if(Segments)
2570 RtlZeroMemory(Segments, SizeOfSegments);
2571
2572 return Segments;
2573 }
2574
2575 static
2576 NTSTATUS
2577 NTAPI
2578 ExeFmtpReadFile(IN PVOID File,
2579 IN PLARGE_INTEGER Offset,
2580 IN ULONG Length,
2581 OUT PVOID * Data,
2582 OUT PVOID * AllocBase,
2583 OUT PULONG ReadSize)
2584 {
2585 NTSTATUS Status;
2586 LARGE_INTEGER FileOffset;
2587 ULONG AdjustOffset;
2588 ULONG OffsetAdjustment;
2589 ULONG BufferSize;
2590 ULONG UsedSize;
2591 PVOID Buffer;
2592
2593 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2594
2595 if(Length == 0)
2596 {
2597 KEBUGCHECK(STATUS_INVALID_PARAMETER_4);
2598 }
2599
2600 FileOffset = *Offset;
2601
2602 /* Negative/special offset: it cannot be used in this context */
2603 if(FileOffset.u.HighPart < 0)
2604 {
2605 KEBUGCHECK(STATUS_INVALID_PARAMETER_5);
2606 }
2607
2608 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2609 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2610 FileOffset.u.LowPart = AdjustOffset;
2611
2612 BufferSize = Length + OffsetAdjustment;
2613 BufferSize = PAGE_ROUND_UP(BufferSize);
2614
2615 /*
2616 * It's ok to use paged pool, because this is a temporary buffer only used in
2617 * the loading of executables. The assumption is that MmCreateSection is
2618 * always called at low IRQLs and that these buffers don't survive a brief
2619 * initialization phase
2620 */
2621 Buffer = ExAllocatePoolWithTag(PagedPool,
2622 BufferSize,
2623 TAG('M', 'm', 'X', 'r'));
2624
2625 UsedSize = 0;
2626
2627 #if 0
2628 Status = MmspPageRead(File,
2629 Buffer,
2630 BufferSize,
2631 &FileOffset,
2632 &UsedSize);
2633 #else
2634 /*
2635 * FIXME: if we don't use ZwReadFile, caching is not enabled for the file and
2636 * nothing will work. But using ZwReadFile is wrong, and using its side effects
2637 * to initialize internal state is even worse. Our cache manager is in need of
2638 * professional help
2639 */
2640 {
2641 IO_STATUS_BLOCK Iosb;
2642
2643 Status = ZwReadFile(File,
2644 NULL,
2645 NULL,
2646 NULL,
2647 &Iosb,
2648 Buffer,
2649 BufferSize,
2650 &FileOffset,
2651 NULL);
2652
2653 if(NT_SUCCESS(Status))
2654 {
2655 UsedSize = Iosb.Information;
2656 }
2657 }
2658 #endif
2659
2660 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2661 {
2662 Status = STATUS_IN_PAGE_ERROR;
2663 ASSERT(!NT_SUCCESS(Status));
2664 }
2665
2666 if(NT_SUCCESS(Status))
2667 {
2668 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2669 *AllocBase = Buffer;
2670 *ReadSize = UsedSize - OffsetAdjustment;
2671 }
2672 else
2673 {
2674 ExFreePool(Buffer);
2675 }
2676
2677 return Status;
2678 }
2679
2680 #ifdef NASSERT
2681 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2682 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2683 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2684 #else
2685 static
2686 VOID
2687 NTAPI
2688 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2689 {
2690 ULONG i;
2691
2692 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2693 {
2694 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2695 ImageSectionObject->Segments[i - 1].VirtualAddress);
2696 }
2697 }
2698
2699 static
2700 VOID
2701 NTAPI
2702 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2703 {
2704 ULONG i;
2705
2706 MmspAssertSegmentsSorted(ImageSectionObject);
2707
2708 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2709 {
2710 ASSERT(ImageSectionObject->Segments[i].Length > 0);
2711
2712 if(i > 0)
2713 {
2714 ASSERT(ImageSectionObject->Segments[i].VirtualAddress >=
2715 (ImageSectionObject->Segments[i - 1].VirtualAddress +
2716 ImageSectionObject->Segments[i - 1].Length));
2717 }
2718 }
2719 }
2720
2721 static
2722 VOID
2723 NTAPI
2724 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2725 {
2726 ULONG i;
2727
2728 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2729 {
2730 ASSERT((ImageSectionObject->Segments[i].VirtualAddress % PAGE_SIZE) == 0);
2731 ASSERT((ImageSectionObject->Segments[i].Length % PAGE_SIZE) == 0);
2732 }
2733 }
2734 #endif
2735
2736 static
2737 int
2738 __cdecl
2739 MmspCompareSegments(const void * x,
2740 const void * y)
2741 {
2742 PMM_SECTION_SEGMENT Segment1 = (PMM_SECTION_SEGMENT)x;
2743 PMM_SECTION_SEGMENT Segment2 = (PMM_SECTION_SEGMENT)y;
2744
2745 return
2746 (Segment1->VirtualAddress - Segment2->VirtualAddress) >>
2747 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
2748 }
2749
2750 /*
2751 * Ensures an image section's segments are sorted in memory
2752 */
2753 static
2754 VOID
2755 NTAPI
2756 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2757 IN ULONG Flags)
2758 {
2759 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2760 {
2761 MmspAssertSegmentsSorted(ImageSectionObject);
2762 }
2763 else
2764 {
2765 qsort(ImageSectionObject->Segments,
2766 ImageSectionObject->NrSegments,
2767 sizeof(ImageSectionObject->Segments[0]),
2768 MmspCompareSegments);
2769 }
2770 }
2771
2772
2773 /*
2774 * Ensures an image section's segments don't overlap in memory and don't have
2775 * gaps and don't have a null size. We let them map to overlapping file regions,
2776 * though - that's not necessarily an error
2777 */
2778 static
2779 BOOLEAN
2780 NTAPI
2781 MmspCheckSegmentBounds
2782 (
2783 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2784 IN ULONG Flags
2785 )
2786 {
2787 ULONG i;
2788
2789 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2790 {
2791 MmspAssertSegmentsNoOverlap(ImageSectionObject);
2792 return TRUE;
2793 }
2794
2795 ASSERT(ImageSectionObject->NrSegments >= 1);
2796
2797 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2798 {
2799 if(ImageSectionObject->Segments[i].Length == 0)
2800 {
2801 return FALSE;
2802 }
2803
2804 if(i > 0)
2805 {
2806 /*
2807 * TODO: relax the limitation on gaps. For example, gaps smaller than a
2808 * page could be OK (Windows seems to be OK with them), and larger gaps
2809 * could lead to image sections spanning several discontiguous regions
2810 * (NtMapViewOfSection could then refuse to map them, and they could
2811 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2812 */
2813 if ((ImageSectionObject->Segments[i - 1].VirtualAddress +
2814 ImageSectionObject->Segments[i - 1].Length) !=
2815 ImageSectionObject->Segments[i].VirtualAddress)
2816 {
2817 return FALSE;
2818 }
2819 }
2820 }
2821
2822 return TRUE;
2823 }
2824
2825 /*
2826 * Merges and pads an image section's segments until they all are page-aligned
2827 * and have a size that is a multiple of the page size
2828 */
2829 static
2830 BOOLEAN
2831 NTAPI
2832 MmspPageAlignSegments
2833 (
2834 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2835 IN ULONG Flags
2836 )
2837 {
2838 ULONG i;
2839 ULONG LastSegment;
2840 BOOLEAN Initialized;
2841 PMM_SECTION_SEGMENT EffectiveSegment;
2842
2843 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
2844 {
2845 MmspAssertSegmentsPageAligned(ImageSectionObject);
2846 return TRUE;
2847 }
2848
2849 Initialized = FALSE;
2850 LastSegment = 0;
2851 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2852
2853 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2854 {
2855 /*
2856 * The first segment requires special handling
2857 */
2858 if (i == 0)
2859 {
2860 ULONG_PTR VirtualAddress;
2861 ULONG_PTR VirtualOffset;
2862
2863 VirtualAddress = EffectiveSegment->VirtualAddress;
2864
2865 /* Round down the virtual address to the nearest page */
2866 EffectiveSegment->VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2867
2868 /* Round up the virtual size to the nearest page */
2869 EffectiveSegment->Length = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length) -
2870 EffectiveSegment->VirtualAddress;
2871
2872 /* Adjust the raw address and size */
2873 VirtualOffset = VirtualAddress - EffectiveSegment->VirtualAddress;
2874
2875 if (EffectiveSegment->FileOffset < VirtualOffset)
2876 {
2877 return FALSE;
2878 }
2879
2880 /*
2881 * Garbage in, garbage out: unaligned base addresses make the file
2882 * offset point in curious and odd places, but that's what we were
2883 * asked for
2884 */
2885 EffectiveSegment->FileOffset -= VirtualOffset;
2886 EffectiveSegment->RawLength += VirtualOffset;
2887 }
2888 else
2889 {
2890 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2891 ULONG_PTR EndOfEffectiveSegment;
2892
2893 EndOfEffectiveSegment = EffectiveSegment->VirtualAddress + EffectiveSegment->Length;
2894 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2895
2896 /*
2897 * The current segment begins exactly where the current effective
2898 * segment ended, therefore beginning a new effective segment
2899 */
2900 if (EndOfEffectiveSegment == Segment->VirtualAddress)
2901 {
2902 LastSegment ++;
2903 ASSERT(LastSegment <= i);
2904 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2905
2906 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2907
2908 if (LastSegment != i)
2909 {
2910 /*
2911 * Copy the current segment. If necessary, the effective segment
2912 * will be expanded later
2913 */
2914 *EffectiveSegment = *Segment;
2915 }
2916
2917 /*
2918 * Page-align the virtual size. We know for sure the virtual address
2919 * already is
2920 */
2921 ASSERT((EffectiveSegment->VirtualAddress % PAGE_SIZE) == 0);
2922 EffectiveSegment->Length = PAGE_ROUND_UP(EffectiveSegment->Length);
2923 }
2924 /*
2925 * The current segment is still part of the current effective segment:
2926 * extend the effective segment to reflect this
2927 */
2928 else if (EndOfEffectiveSegment > Segment->VirtualAddress)
2929 {
2930 static const ULONG FlagsToProtection[16] =
2931 {
2932 PAGE_NOACCESS,
2933 PAGE_READONLY,
2934 PAGE_READWRITE,
2935 PAGE_READWRITE,
2936 PAGE_EXECUTE_READ,
2937 PAGE_EXECUTE_READ,
2938 PAGE_EXECUTE_READWRITE,
2939 PAGE_EXECUTE_READWRITE,
2940 PAGE_WRITECOPY,
2941 PAGE_WRITECOPY,
2942 PAGE_WRITECOPY,
2943 PAGE_WRITECOPY,
2944 PAGE_EXECUTE_WRITECOPY,
2945 PAGE_EXECUTE_WRITECOPY,
2946 PAGE_EXECUTE_WRITECOPY,
2947 PAGE_EXECUTE_WRITECOPY
2948 };
2949
2950 unsigned ProtectionFlags;
2951
2952 /*
2953 * Extend the file size
2954 */
2955
2956 /* Unaligned segments must be contiguous within the file */
2957 if (Segment->FileOffset != (EffectiveSegment->FileOffset +
2958 EffectiveSegment->RawLength))
2959 {
2960 return FALSE;
2961 }
2962
2963 EffectiveSegment->RawLength += Segment->RawLength;
2964
2965 /*
2966 * Extend the virtual size
2967 */
2968 ASSERT(PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) >= EndOfEffectiveSegment);
2969
2970 EffectiveSegment->Length = PAGE_ROUND_UP(Segment->VirtualAddress + Segment->Length) -
2971 EffectiveSegment->VirtualAddress;
2972
2973 /*
2974 * Merge the protection
2975 */
2976 EffectiveSegment->Protection |= Segment->Protection;
2977
2978 /* Clean up redundance */
2979 ProtectionFlags = 0;
2980
2981 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2982 ProtectionFlags |= 1 << 0;
2983
2984 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2985 ProtectionFlags |= 1 << 1;
2986
2987 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2988 ProtectionFlags |= 1 << 2;
2989
2990 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2991 ProtectionFlags |= 1 << 3;
2992
2993 ASSERT(ProtectionFlags < 16);
2994 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2995
2996 /* If a segment was required to be shared and cannot, fail */
2997 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2998 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2999 {
3000 return FALSE;
3001 }
3002 }
3003 /*
3004 * We assume no holes between segments at this point
3005 */
3006 else
3007 {
3008 ASSERT(FALSE);
3009 }
3010 }
3011 }
3012 ImageSectionObject->NrSegments = LastSegment + 1;
3013
3014 return TRUE;
3015 }
3016
3017 NTSTATUS
3018 ExeFmtpCreateImageSection(HANDLE FileHandle,
3019 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3020 {
3021 LARGE_INTEGER Offset;
3022 PVOID FileHeader;
3023 PVOID FileHeaderBuffer;
3024 ULONG FileHeaderSize;
3025 ULONG Flags;
3026 ULONG OldNrSegments;
3027 NTSTATUS Status;
3028 ULONG i;
3029
3030 /*
3031 * Read the beginning of the file (2 pages). Should be enough to contain
3032 * all (or most) of the headers
3033 */
3034 Offset.QuadPart = 0;
3035
3036 /* FIXME: use FileObject instead of FileHandle */
3037 Status = ExeFmtpReadFile (FileHandle,
3038 &Offset,
3039 PAGE_SIZE * 2,
3040 &FileHeader,
3041 &FileHeaderBuffer,
3042 &FileHeaderSize);
3043
3044 if (!NT_SUCCESS(Status))
3045 return Status;
3046
3047 if (FileHeaderSize == 0)
3048 {
3049 ExFreePool(FileHeaderBuffer);
3050 return STATUS_UNSUCCESSFUL;
3051 }
3052
3053 /*
3054 * Look for a loader that can handle this executable
3055 */
3056 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3057 {
3058 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3059 Flags = 0;
3060
3061 /* FIXME: use FileObject instead of FileHandle */
3062 Status = ExeFmtpLoaders[i](FileHeader,
3063 FileHeaderSize,
3064 FileHandle,
3065 ImageSectionObject,
3066 &Flags,
3067 ExeFmtpReadFile,
3068 ExeFmtpAllocateSegments);
3069
3070 if (!NT_SUCCESS(Status))
3071 {
3072 if (ImageSectionObject->Segments)
3073 {
3074 ExFreePool(ImageSectionObject->Segments);
3075 ImageSectionObject->Segments = NULL;
3076 }
3077 }
3078
3079 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3080 break;
3081 }
3082
3083 ExFreePool(FileHeaderBuffer);
3084
3085 /*
3086 * No loader handled the format
3087 */
3088 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3089 {
3090 Status = STATUS_INVALID_IMAGE_NOT_MZ;
3091 ASSERT(!NT_SUCCESS(Status));
3092 }
3093
3094 if (!NT_SUCCESS(Status))
3095 return Status;
3096
3097 ASSERT(ImageSectionObject->Segments != NULL);
3098
3099 /*
3100 * Some defaults
3101 */
3102 /* FIXME? are these values platform-dependent? */
3103 if(ImageSectionObject->StackReserve == 0)
3104 ImageSectionObject->StackReserve = 0x40000;
3105
3106 if(ImageSectionObject->StackCommit == 0)
3107 ImageSectionObject->StackCommit = 0x1000;
3108
3109 if(ImageSectionObject->ImageBase == 0)
3110 {
3111 if(ImageSectionObject->ImageCharacteristics & IMAGE_FILE_DLL)
3112 ImageSectionObject->ImageBase = 0x10000000;
3113 else
3114 ImageSectionObject->ImageBase = 0x00400000;
3115 }
3116
3117 /*
3118 * And now the fun part: fixing the segments
3119 */
3120
3121 /* Sort them by virtual address */
3122 MmspSortSegments(ImageSectionObject, Flags);
3123
3124 /* Ensure they don't overlap in memory */
3125 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3126 return STATUS_INVALID_IMAGE_FORMAT;
3127
3128 /* Ensure they are aligned */
3129 OldNrSegments = ImageSectionObject->NrSegments;
3130
3131 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3132 return STATUS_INVALID_IMAGE_FORMAT;
3133
3134 /* Trim them if the alignment phase merged some of them */
3135 if (ImageSectionObject->NrSegments < OldNrSegments)
3136 {
3137 PMM_SECTION_SEGMENT Segments;
3138 SIZE_T SizeOfSegments;
3139
3140 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3141
3142 Segments = ExAllocatePoolWithTag(PagedPool,
3143 SizeOfSegments,
3144 TAG_MM_SECTION_SEGMENT);
3145
3146 if (Segments == NULL)
3147 return STATUS_INSUFFICIENT_RESOURCES;
3148
3149 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3150 ExFreePool(ImageSectionObject->Segments);
3151 ImageSectionObject->Segments = Segments;
3152 }
3153
3154 /* And finish their initialization */
3155 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3156 {
3157 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3158 ImageSectionObject->Segments[i].ReferenceCount = 1;
3159
3160 RtlZeroMemory(&ImageSectionObject->Segments[i].PageDirectory,
3161 sizeof(ImageSectionObject->Segments[i].PageDirectory));
3162 }
3163
3164 ASSERT(NT_SUCCESS(Status));
3165 return Status;
3166 }
3167
3168 NTSTATUS
3169 MmCreateImageSection(PSECTION_OBJECT *SectionObject,
3170 ACCESS_MASK DesiredAccess,
3171 POBJECT_ATTRIBUTES ObjectAttributes,
3172 PLARGE_INTEGER UMaximumSize,
3173 ULONG SectionPageProtection,
3174 ULONG AllocationAttributes,
3175 HANDLE FileHandle)
3176 {
3177 PSECTION_OBJECT Section;
3178 NTSTATUS Status;
3179 PFILE_OBJECT FileObject;
3180 PMM_SECTION_SEGMENT SectionSegments;
3181 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3182 ULONG i;
3183 ULONG FileAccess = 0;
3184
3185 /*
3186 * Specifying a maximum size is meaningless for an image section
3187 */
3188 if (UMaximumSize != NULL)
3189 {
3190 return(STATUS_INVALID_PARAMETER_4);
3191 }
3192
3193 /*
3194 * Check file access required
3195 */
3196 if (SectionPageProtection & PAGE_READWRITE ||
3197 SectionPageProtection & PAGE_EXECUTE_READWRITE)
3198 {
3199 FileAccess = FILE_READ_DATA | FILE_WRITE_DATA;
3200 }
3201 else
3202 {
3203 FileAccess = FILE_READ_DATA;
3204 }
3205
3206 /*
3207 * Reference the file handle
3208 */
3209 Status = ObReferenceObjectByHandle(FileHandle,
3210 FileAccess,
3211 IoFileObjectType,
3212 UserMode,
3213 (PVOID*)(PVOID)&FileObject,
3214 NULL);
3215
3216 if (!NT_SUCCESS(Status))
3217 {
3218 return Status;
3219 }
3220
3221 /*
3222 * Create the section
3223 */
3224 Status = ObCreateObject (ExGetPreviousMode(),
3225 MmSectionObjectType,
3226 ObjectAttributes,
3227 ExGetPreviousMode(),
3228 NULL,
3229 sizeof(SECTION_OBJECT),
3230 0,
3231 0,
3232 (PVOID*)(PVOID)&Section);
3233 if (!NT_SUCCESS(Status))
3234 {
3235 ObDereferenceObject(FileObject);
3236 return(Status);
3237 }
3238
3239 /*
3240 * Initialize it
3241 */
3242 Section->SectionPageProtection = SectionPageProtection;
3243 Section->AllocationAttributes = AllocationAttributes;
3244
3245 /*
3246 * Initialized caching for this file object if previously caching
3247 * was initialized for the same on disk file
3248 */
3249 Status = CcTryToInitializeFileCache(FileObject);
3250
3251 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
3252 {
3253 NTSTATUS StatusExeFmt;
3254
3255 ImageSectionObject = ExAllocatePoolWithTag(PagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3256 if (ImageSectionObject == NULL)
3257 {
3258 ObDereferenceObject(FileObject);
3259 ObDereferenceObject(Section);
3260 return(STATUS_NO_MEMORY);
3261 }
3262
3263 RtlZeroMemory(ImageSectionObject, sizeof(MM_IMAGE_SECTION_OBJECT));
3264
3265 StatusExeFmt = ExeFmtpCreateImageSection(FileHandle, ImageSectionObject);
3266
3267 if (!NT_SUCCESS(StatusExeFmt))
3268 {
3269 if(ImageSectionObject->Segments != NULL)
3270 ExFreePool(ImageSectionObject->Segments);
3271
3272 ExFreePool(ImageSectionObject);
3273 ObDereferenceObject(Section);
3274 ObDereferenceObject(FileObject);
3275 return(StatusExeFmt);
3276 }
3277
3278 Section->ImageSection = ImageSectionObject;
3279 ASSERT(ImageSectionObject->Segments);
3280
3281 /*
3282 * Lock the file
3283 */
3284 Status = MmspWaitForFileLock(FileObject);
3285 if (!NT_SUCCESS(Status))
3286 {
3287 ExFreePool(ImageSectionObject->Segments);
3288 ExFreePool(ImageSectionObject);
3289 ObDereferenceObject(Section);
3290 ObDereferenceObject(FileObject);
3291 return(Status);
3292 }
3293
3294 if (NULL != InterlockedCompareExchangePointer(&FileObject->SectionObjectPointer->ImageSectionObject,
3295 ImageSectionObject, NULL))
3296 {
3297 /*
3298 * An other thread has initialized the some image in the background
3299 */
3300 ExFreePool(ImageSectionObject->Segments);
3301 ExFreePool(ImageSectionObject);
3302 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3303 Section->ImageSection = ImageSectionObject;
3304 SectionSegments = ImageSectionObject->Segments;
3305
3306 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3307 {
3308 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3309 }
3310 }
3311
3312 Status = StatusExeFmt;
3313 }
3314 else
3315 {
3316 /*
3317 * Lock the file
3318 */
3319 Status = MmspWaitForFileLock(FileObject);
3320 if (Status != STATUS_SUCCESS)
3321 {
3322 ObDereferenceObject(Section);
3323 ObDereferenceObject(FileObject);
3324 return(Status);
3325 }
3326
3327 ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3328 Section->ImageSection = ImageSectionObject;
3329 SectionSegments = ImageSectionObject->Segments;
3330
3331 /*
3332 * Otherwise just reference all the section segments
3333 */
3334 for (i = 0; i < ImageSectionObject->NrSegments; i++)
3335 {
3336 InterlockedIncrementUL(&SectionSegments[i].ReferenceCount);
3337 }
3338
3339 Status = STATUS_SUCCESS;
3340 }
3341 Section->FileObject = FileObject;
3342 CcRosReferenceCache(FileObject);
3343 KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3344 *SectionObject = Section;
3345 return(Status);
3346 }
3347
3348 /*
3349 * @implemented
3350 */
3351 NTSTATUS STDCALL
3352 NtCreateSection (OUT PHANDLE SectionHandle,
3353 IN ACCESS_MASK DesiredAccess,
3354 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
3355 IN PLARGE_INTEGER MaximumSize OPTIONAL,
3356 IN ULONG SectionPageProtection OPTIONAL,
3357 IN ULONG AllocationAttributes,
3358 IN HANDLE FileHandle OPTIONAL)
3359 {
3360 LARGE_INTEGER SafeMaximumSize;
3361 PSECTION_OBJECT SectionObject;
3362 KPROCESSOR_MODE PreviousMode;
3363 NTSTATUS Status = STATUS_SUCCESS;
3364
3365 PreviousMode = ExGetPreviousMode();
3366
3367 if(MaximumSize != NULL && PreviousMode != KernelMode)
3368 {
3369 _SEH_TRY
3370 {
3371 /* make a copy on the stack */
3372 SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
3373 MaximumSize = &SafeMaximumSize;
3374 }
3375 _SEH_HANDLE
3376 {
3377 Status = _SEH_GetExceptionCode();
3378 }
3379 _SEH_END;
3380
3381 if(!NT_SUCCESS(Status))
3382 {
3383 return Status;
3384 }
3385 }
3386
3387 Status = MmCreateSection(&SectionObject,
3388 DesiredAccess,
3389 ObjectAttributes,
3390 MaximumSize,
3391 SectionPageProtection,
3392 AllocationAttributes,
3393 FileHandle,
3394 NULL);
3395
3396 if (NT_SUCCESS(Status))
3397 {
3398 Status = ObInsertObject ((PVOID)SectionObject,
3399 NULL,
3400 DesiredAccess,
3401 0,
3402 NULL,
3403 SectionHandle);
3404 ObDereferenceObject(SectionObject);