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