xref: /reactos/ntoskrnl/mm/section.c (revision 7f26a396)
1 /*
2  * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  *
18  *
19  * PROJECT:         ReactOS kernel
20  * PURPOSE:         Implements section objects
21  *
22  * PROGRAMMERS:     Rex Jolliff
23  *                  David Welch
24  *                  Eric Kohl
25  *                  Emanuele Aliberti
26  *                  Eugene Ingerman
27  *                  Casper Hornstrup
28  *                  KJK::Hyperion
29  *                  Guido de Jong
30  *                  Ge van Geldorp
31  *                  Royce Mitchell III
32  *                  Filip Navara
33  *                  Aleksey Bragin
34  *                  Jason Filby
35  *                  Thomas Weidenmueller
36  *                  Gunnar Andre' Dalsnes
37  *                  Mike Nordell
38  *                  Alex Ionescu
39  *                  Gregor Anich
40  *                  Steven Edwards
41  *                  Herve Poussineau
42  */
43 
44 /* INCLUDES *****************************************************************/
45 
46 #include <ntoskrnl.h>
47 #include <cache/newcc.h>
48 #include <cache/section/newmm.h>
49 #define NDEBUG
50 #include <debug.h>
51 #include <reactos/exeformat.h>
52 
53 #include "ARM3/miarm.h"
54 
55 #undef MmSetPageEntrySectionSegment
56 #define MmSetPageEntrySectionSegment(S,O,E) do { \
57         DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
58         _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__);   \
59 	} while (0)
60 
61 extern MMSESSION MmSession;
62 
63 static LARGE_INTEGER TinyTime = {{-1L, -1L}};
64 
65 #ifndef NEWCC
66 KEVENT MmWaitPageEvent;
67 
68 VOID
69 NTAPI
70 _MmLockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
71 {
72     //DPRINT("MmLockSectionSegment(%p,%s:%d)\n", Segment, file, line);
73     ExAcquireFastMutex(&Segment->Lock);
74     Segment->Locked = TRUE;
75 }
76 
77 VOID
78 NTAPI
79 _MmUnlockSectionSegment(PMM_SECTION_SEGMENT Segment, const char *file, int line)
80 {
81     ASSERT(Segment->Locked);
82     Segment->Locked = FALSE;
83     ExReleaseFastMutex(&Segment->Lock);
84     //DPRINT("MmUnlockSectionSegment(%p,%s:%d)\n", Segment, file, line);
85 }
86 #endif
87 
88 static
89 PMM_SECTION_SEGMENT
90 MiGrabDataSection(PSECTION_OBJECT_POINTERS SectionObjectPointer)
91 {
92     KIRQL OldIrql = MiAcquirePfnLock();
93     PMM_SECTION_SEGMENT Segment = NULL;
94 
95     while (TRUE)
96     {
97         Segment = SectionObjectPointer->DataSectionObject;
98         if (!Segment)
99             break;
100 
101         if (Segment->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE))
102         {
103             MiReleasePfnLock(OldIrql);
104             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
105             OldIrql = MiAcquirePfnLock();
106             continue;
107         }
108 
109         ASSERT(Segment->SegFlags & MM_DATAFILE_SEGMENT);
110         InterlockedIncrement64(&Segment->RefCount);
111         break;
112     }
113 
114     MiReleasePfnLock(OldIrql);
115 
116     return Segment;
117 }
118 
119 /* Somewhat grotesque, but eh... */
120 PMM_IMAGE_SECTION_OBJECT ImageSectionObjectFromSegment(PMM_SECTION_SEGMENT Segment)
121 {
122     ASSERT((Segment->SegFlags & MM_DATAFILE_SEGMENT) == 0);
123 
124     return CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
125 }
126 
127 NTSTATUS
128 MiMapViewInSystemSpace(IN PVOID Section,
129                        IN PVOID Session,
130                        OUT PVOID *MappedBase,
131                        IN OUT PSIZE_T ViewSize,
132                        IN PLARGE_INTEGER SectionOffset);
133 
134 NTSTATUS
135 NTAPI
136 MmCreateArm3Section(OUT PVOID *SectionObject,
137                     IN ACCESS_MASK DesiredAccess,
138                     IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
139                     IN PLARGE_INTEGER InputMaximumSize,
140                     IN ULONG SectionPageProtection,
141                     IN ULONG AllocationAttributes,
142                     IN HANDLE FileHandle OPTIONAL,
143                     IN PFILE_OBJECT FileObject OPTIONAL);
144 
145 NTSTATUS
146 NTAPI
147 MmMapViewOfArm3Section(IN PVOID SectionObject,
148                        IN PEPROCESS Process,
149                        IN OUT PVOID *BaseAddress,
150                        IN ULONG_PTR ZeroBits,
151                        IN SIZE_T CommitSize,
152                        IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
153                        IN OUT PSIZE_T ViewSize,
154                        IN SECTION_INHERIT InheritDisposition,
155                        IN ULONG AllocationType,
156                        IN ULONG Protect);
157 
158 //
159 // PeFmtCreateSection depends on the following:
160 //
161 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
162 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
163 
164 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
165 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
166 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
167 
168 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
169 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
170 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
171 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
172 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
173 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
174 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
175 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
176 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
177 
178 /* TYPES *********************************************************************/
179 
180 typedef struct
181 {
182     PMEMORY_AREA MemoryArea;
183     PMM_SECTION_SEGMENT Segment;
184     LARGE_INTEGER Offset;
185     BOOLEAN WasDirty;
186     BOOLEAN Private;
187     PEPROCESS CallingProcess;
188     ULONG_PTR SectionEntry;
189 }
190 MM_SECTION_PAGEOUT_CONTEXT;
191 
192 /* GLOBALS *******************************************************************/
193 
194 POBJECT_TYPE MmSectionObjectType = NULL;
195 
196 ULONG_PTR MmSubsectionBase;
197 
198 static ULONG SectionCharacteristicsToProtect[16] =
199 {
200     PAGE_NOACCESS,          /* 0 = NONE */
201     PAGE_NOACCESS,          /* 1 = SHARED */
202     PAGE_EXECUTE,           /* 2 = EXECUTABLE */
203     PAGE_EXECUTE,           /* 3 = EXECUTABLE, SHARED */
204     PAGE_READONLY,          /* 4 = READABLE */
205     PAGE_READONLY,          /* 5 = READABLE, SHARED */
206     PAGE_EXECUTE_READ,      /* 6 = READABLE, EXECUTABLE */
207     PAGE_EXECUTE_READ,      /* 7 = READABLE, EXECUTABLE, SHARED */
208     /*
209      * FIXME? do we really need the WriteCopy field in segments? can't we use
210      * PAGE_WRITECOPY here?
211      */
212     PAGE_READWRITE,         /* 8 = WRITABLE */
213     PAGE_READWRITE,         /* 9 = WRITABLE, SHARED */
214     PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
215     PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
216     PAGE_READWRITE,         /* 12 = WRITABLE, READABLE */
217     PAGE_READWRITE,         /* 13 = WRITABLE, READABLE, SHARED */
218     PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
219     PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
220 };
221 
222 extern ACCESS_MASK MmMakeFileAccess[8];
223 static GENERIC_MAPPING MmpSectionMapping =
224 {
225     STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
226     STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
227     STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
228     SECTION_ALL_ACCESS
229 };
230 
231 
232 /* FUNCTIONS *****************************************************************/
233 
234 
235 
236 NTSTATUS
237 NTAPI
238 MiWritePage(PMM_SECTION_SEGMENT Segment,
239             LONGLONG SegOffset,
240             PFN_NUMBER Page)
241 /*
242  * FUNCTION: write a page for a section backed memory area.
243  * PARAMETERS:
244  *       MemoryArea - Memory area to write the page for.
245  *       Offset - Offset of the page to write.
246  *       Page - Page which contains the data to write.
247  */
248 {
249     NTSTATUS Status;
250     IO_STATUS_BLOCK IoStatus;
251     KEVENT Event;
252     UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
253     PMDL Mdl = (PMDL)MdlBase;
254     PFILE_OBJECT FileObject = Segment->FileObject;
255     LARGE_INTEGER FileOffset;
256 
257     FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
258 
259     RtlZeroMemory(MdlBase, sizeof(MdlBase));
260     MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
261     MmBuildMdlFromPages(Mdl, &Page);
262     Mdl->MdlFlags |= MDL_PAGES_LOCKED;
263 
264     KeInitializeEvent(&Event, NotificationEvent, FALSE);
265     Status = IoSynchronousPageWrite(FileObject, Mdl, &FileOffset, &Event, &IoStatus);
266     if (Status == STATUS_PENDING)
267     {
268         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
269         Status = IoStatus.Status;
270     }
271     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
272     {
273         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
274     }
275 
276     return Status;
277 }
278 
279 
280 /*
281  References:
282   [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
283       File Format Specification", revision 6.0 (February 1999)
284 */
285 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
286                                   IN SIZE_T FileHeaderSize,
287                                   IN PVOID File,
288                                   OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
289                                   OUT PULONG Flags,
290                                   IN PEXEFMT_CB_READ_FILE ReadFileCb,
291                                   IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
292 {
293     NTSTATUS nStatus;
294     ULONG cbFileHeaderOffsetSize = 0;
295     ULONG cbSectionHeadersOffset = 0;
296     ULONG cbSectionHeadersSize;
297     ULONG cbSectionHeadersOffsetSize = 0;
298     ULONG cbOptHeaderSize;
299     ULONG cbHeadersSize = 0;
300     ULONG nSectionAlignment;
301     ULONG nFileAlignment;
302     ULONG_PTR ImageBase = 0;
303     const IMAGE_DOS_HEADER * pidhDosHeader;
304     const IMAGE_NT_HEADERS32 * pinhNtHeader;
305     const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
306     const IMAGE_SECTION_HEADER * pishSectionHeaders;
307     PMM_SECTION_SEGMENT pssSegments;
308     LARGE_INTEGER lnOffset;
309     PVOID pBuffer;
310     SIZE_T nPrevVirtualEndOfSegment = 0;
311     ULONG nFileSizeOfHeaders = 0;
312     ULONG i;
313     ULONG AlignedLength;
314 
315     ASSERT(FileHeader);
316     ASSERT(FileHeaderSize > 0);
317     ASSERT(File);
318     ASSERT(ImageSectionObject);
319     ASSERT(ReadFileCb);
320     ASSERT(AllocateSegmentsCb);
321 
322     ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
323 
324     ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
325 
326 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
327 
328     pBuffer = NULL;
329     pidhDosHeader = FileHeader;
330 
331     /* DOS HEADER */
332     nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
333 
334     /* image too small to be an MZ executable */
335     if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
336         DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
337 
338     /* no MZ signature */
339     if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
340         DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
341 
342     /* NT HEADER */
343     nStatus = STATUS_INVALID_IMAGE_PROTECT;
344 
345     /* not a Windows executable */
346     if(pidhDosHeader->e_lfanew <= 0)
347         DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
348 
349     if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
350         DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
351 
352     if(FileHeaderSize < cbFileHeaderOffsetSize)
353         pinhNtHeader = NULL;
354     else
355     {
356         /*
357          * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
358          * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
359          */
360         ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
361         pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
362     }
363 
364     /*
365      * the buffer doesn't contain the NT file header, or the alignment is wrong: we
366      * need to read the header from the file
367      */
368     if(FileHeaderSize < cbFileHeaderOffsetSize ||
369             (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
370     {
371         ULONG cbNtHeaderSize;
372         ULONG cbReadSize;
373         PVOID pData;
374 
375 l_ReadHeaderFromFile:
376         cbNtHeaderSize = 0;
377         lnOffset.QuadPart = pidhDosHeader->e_lfanew;
378 
379         /* read the header from the file */
380         nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
381 
382         if(!NT_SUCCESS(nStatus))
383         {
384             NTSTATUS ReturnedStatus = nStatus;
385 
386             /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
387             if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
388 
389             DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
390         }
391 
392         ASSERT(pData);
393         ASSERT(pBuffer);
394         ASSERT(cbReadSize > 0);
395 
396         nStatus = STATUS_INVALID_IMAGE_FORMAT;
397 
398         /* the buffer doesn't contain the file header */
399         if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
400             DIE(("The file doesn't contain the PE file header\n"));
401 
402         pinhNtHeader = pData;
403 
404         /* object still not aligned: copy it to the beginning of the buffer */
405         if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
406         {
407             ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
408             RtlMoveMemory(pBuffer, pData, cbReadSize);
409             pinhNtHeader = pBuffer;
410         }
411 
412         /* invalid NT header */
413         nStatus = STATUS_INVALID_IMAGE_PROTECT;
414 
415         if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
416             DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
417 
418         nStatus = STATUS_INVALID_IMAGE_FORMAT;
419 
420         if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
421             DIE(("The full NT header is too large\n"));
422 
423         /* the buffer doesn't contain the whole NT header */
424         if(cbReadSize < cbNtHeaderSize)
425             DIE(("The file doesn't contain the full NT header\n"));
426     }
427     else
428     {
429         ULONG cbOptHeaderOffsetSize = 0;
430 
431         nStatus = STATUS_INVALID_IMAGE_PROTECT;
432 
433         /* don't trust an invalid NT header */
434         if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
435             DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
436 
437         if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
438             DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
439 
440         nStatus = STATUS_INVALID_IMAGE_FORMAT;
441 
442         if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
443             DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
444 
445         /* the buffer doesn't contain the whole NT header: read it from the file */
446         if(cbOptHeaderOffsetSize > FileHeaderSize)
447             goto l_ReadHeaderFromFile;
448     }
449 
450     /* read information from the NT header */
451     piohOptHeader = &pinhNtHeader->OptionalHeader;
452     cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
453 
454     nStatus = STATUS_INVALID_IMAGE_FORMAT;
455 
456     if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
457         DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
458 
459     /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
460 
461     switch(piohOptHeader->Magic)
462     {
463         case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
464 #ifndef _WIN64
465             nStatus = STATUS_INVALID_IMAGE_WIN_64;
466             DIE(("Win64 optional header, unsupported\n"));
467 #else
468             // Fall through.
469 #endif
470         case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
471             break;
472         default:
473             DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
474     }
475 
476     if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
477             RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
478     {
479         /* See [1], section 3.4.2 */
480         if(piohOptHeader->SectionAlignment < PAGE_SIZE)
481         {
482             if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
483                 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
484         }
485         else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
486             DIE(("The section alignment is smaller than the file alignment\n"));
487 
488         nSectionAlignment = piohOptHeader->SectionAlignment;
489         nFileAlignment = piohOptHeader->FileAlignment;
490 
491         if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
492             DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
493     }
494     else
495     {
496         nSectionAlignment = PAGE_SIZE;
497         nFileAlignment = PAGE_SIZE;
498     }
499 
500     ASSERT(IsPowerOf2(nSectionAlignment));
501     ASSERT(IsPowerOf2(nFileAlignment));
502 
503     switch(piohOptHeader->Magic)
504     {
505     /* PE32 */
506     case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
507     {
508         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
509             ImageBase = piohOptHeader->ImageBase;
510 
511         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
512             ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
513 
514         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
515             ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
516 
517         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
518             ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
519 
520         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
521         {
522             ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
523 
524             if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
525                     RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
526             {
527                 ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
528                 ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
529             }
530         }
531 
532         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
533         {
534             ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
535                     piohOptHeader->AddressOfEntryPoint);
536         }
537 
538         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
539             ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
540         else
541             ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
542 
543         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
544         {
545             if (piohOptHeader->AddressOfEntryPoint == 0)
546             {
547                 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
548             }
549         }
550 
551         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
552             ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
553 
554         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
555         {
556             ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
557 
558             /*
559              * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
560              * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
561              * magic to any binary.
562              *
563              * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
564              * but honestly that's not tested. It will also break them when running no ReactOS once we implement
565              * the SxS support -- at which point, duh, this should be removed.
566              *
567              * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
568              */
569             ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
570         }
571 
572         break;
573     }
574 #ifdef _WIN64
575     /* PE64 */
576     case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
577     {
578         const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
579 
580         pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
581 
582         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
583         {
584             ImageBase = pioh64OptHeader->ImageBase;
585             if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
586                 DIE(("ImageBase exceeds the address space\n"));
587         }
588 
589         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
590         {
591             if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
592                 DIE(("SizeOfImage exceeds the address space\n"));
593 
594             ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
595         }
596 
597         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
598         {
599             if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
600                 DIE(("SizeOfStackReserve exceeds the address space\n"));
601 
602             ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
603         }
604 
605         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
606         {
607             if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
608                 DIE(("SizeOfStackCommit exceeds the address space\n"));
609 
610             ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
611         }
612 
613         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
614         {
615             ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
616 
617             if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
618                     RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
619             {
620                 ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
621                 ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
622             }
623         }
624 
625         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
626         {
627             ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
628                     pioh64OptHeader->AddressOfEntryPoint);
629         }
630 
631         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
632             ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
633         else
634             ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
635 
636         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
637         {
638             if (pioh64OptHeader->AddressOfEntryPoint == 0)
639             {
640                 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
641             }
642         }
643 
644         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
645             ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
646 
647         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
648             ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
649 
650         break;
651     }
652 #endif // _WIN64
653     }
654 
655     /* [1], section 3.4.2 */
656     if((ULONG_PTR)ImageBase % 0x10000)
657         DIE(("ImageBase is not aligned on a 64KB boundary"));
658 
659     ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
660     ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
661     ImageSectionObject->ImageInformation.GpValue = 0;
662     ImageSectionObject->ImageInformation.ZeroBits = 0;
663     ImageSectionObject->BasedAddress = (PVOID)ImageBase;
664 
665     /* SECTION HEADERS */
666     nStatus = STATUS_INVALID_IMAGE_FORMAT;
667 
668     /* see [1], section 3.3 */
669     if(pinhNtHeader->FileHeader.NumberOfSections > 96)
670         DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
671 
672     /*
673      * the additional segment is for the file's headers. They need to be present for
674      * the benefit of the dynamic loader (to locate exports, defaults for thread
675      * parameters, resources, etc.)
676      */
677     ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
678 
679     /* file offset for the section headers */
680     if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
681         DIE(("Offset overflow\n"));
682 
683     if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
684         DIE(("Offset overflow\n"));
685 
686     /* size of the section headers */
687     ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
688     cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
689 
690     if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
691         DIE(("Section headers too large\n"));
692 
693     /* size of the executable's headers */
694     if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
695     {
696 //        if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
697 //            DIE(("SizeOfHeaders is not aligned\n"));
698 
699         if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
700             DIE(("The section headers overflow SizeOfHeaders\n"));
701 
702         cbHeadersSize = piohOptHeader->SizeOfHeaders;
703     }
704     else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
705         DIE(("Overflow aligning the size of headers\n"));
706 
707     if(pBuffer)
708     {
709         ExFreePool(pBuffer);
710         pBuffer = NULL;
711     }
712     /* WARNING: pinhNtHeader IS NO LONGER USABLE */
713     /* WARNING: piohOptHeader IS NO LONGER USABLE */
714     /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
715 
716     if(FileHeaderSize < cbSectionHeadersOffsetSize)
717         pishSectionHeaders = NULL;
718     else
719     {
720         /*
721          * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
722          * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
723          */
724         ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
725         pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
726     }
727 
728     /*
729      * the buffer doesn't contain the section headers, or the alignment is wrong:
730      * read the headers from the file
731      */
732     if(FileHeaderSize < cbSectionHeadersOffsetSize ||
733             (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
734     {
735         PVOID pData;
736         ULONG cbReadSize;
737 
738         lnOffset.QuadPart = cbSectionHeadersOffset;
739 
740         /* read the header from the file */
741         nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
742 
743         if(!NT_SUCCESS(nStatus))
744             DIE(("ReadFile failed with status %08X\n", nStatus));
745 
746         ASSERT(pData);
747         ASSERT(pBuffer);
748         ASSERT(cbReadSize > 0);
749 
750         nStatus = STATUS_INVALID_IMAGE_FORMAT;
751 
752         /* the buffer doesn't contain all the section headers */
753         if(cbReadSize < cbSectionHeadersSize)
754             DIE(("The file doesn't contain all of the section headers\n"));
755 
756         pishSectionHeaders = pData;
757 
758         /* object still not aligned: copy it to the beginning of the buffer */
759         if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
760         {
761             ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
762             RtlMoveMemory(pBuffer, pData, cbReadSize);
763             pishSectionHeaders = pBuffer;
764         }
765     }
766 
767     /* SEGMENTS */
768     /* allocate the segments */
769     nStatus = STATUS_INSUFFICIENT_RESOURCES;
770     ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
771 
772     if(ImageSectionObject->Segments == NULL)
773         DIE(("AllocateSegments failed\n"));
774 
775     /* initialize the headers segment */
776     pssSegments = ImageSectionObject->Segments;
777 
778 //  ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
779 
780     if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
781         DIE(("Cannot align the size of the section headers\n"));
782 
783     nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
784     if (nPrevVirtualEndOfSegment < cbHeadersSize)
785         DIE(("Cannot align the size of the section headers\n"));
786 
787     pssSegments[0].Image.FileOffset = 0;
788     pssSegments[0].Protection = PAGE_READONLY;
789     pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
790     pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
791     pssSegments[0].Image.VirtualAddress = 0;
792     pssSegments[0].Image.Characteristics = 0;
793     pssSegments[0].WriteCopy = TRUE;
794 
795     /* skip the headers segment */
796     ++ pssSegments;
797 
798     nStatus = STATUS_INVALID_IMAGE_FORMAT;
799 
800     ASSERT(ImageSectionObject->RefCount > 0);
801 
802     /* convert the executable sections into segments. See also [1], section 4 */
803     for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
804     {
805         ULONG nCharacteristics;
806 
807         /* validate the alignment */
808         if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
809             DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
810 
811         /* sections must be contiguous, ordered by base address and non-overlapping */
812         if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
813             DIE(("Memory gap between section %u and the previous\n", i));
814 
815         /* ignore explicit BSS sections */
816         if(pishSectionHeaders[i].PointerToRawData != 0 && pishSectionHeaders[i].SizeOfRawData != 0)
817         {
818             /* validate the alignment */
819 #if 0
820             /* Yes, this should be a multiple of FileAlignment, but there's
821              * stuff out there that isn't. We can cope with that
822              */
823             if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
824                 DIE(("SizeOfRawData[%u] is not aligned\n", i));
825 #endif
826 
827 //            if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
828 //                DIE(("PointerToRawData[%u] is not aligned\n", i));
829 
830             if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
831                 DIE(("SizeOfRawData[%u] too large\n", i));
832 
833             /* conversion */
834             pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
835             pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
836         }
837         else
838         {
839             /* FIXME: Should reset PointerToRawData to 0 in the image mapping */
840             ASSERT(pssSegments[i].Image.FileOffset == 0);
841             ASSERT(pssSegments[i].RawLength.QuadPart == 0);
842         }
843 
844         ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
845 
846         nCharacteristics = pishSectionHeaders[i].Characteristics;
847 
848         /* no explicit protection */
849         if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
850         {
851             if(nCharacteristics & IMAGE_SCN_CNT_CODE)
852                 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
853 
854             if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
855                 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
856 
857             if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
858                 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
859         }
860 
861         /* see table above */
862         pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
863         pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
864 
865         if(pishSectionHeaders[i].Misc.VirtualSize == 0)
866             pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
867         else
868             pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
869 
870         AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
871         if(AlignedLength < pssSegments[i].Length.LowPart)
872             DIE(("Cannot align the virtual size of section %u\n", i));
873 
874         pssSegments[i].Length.LowPart = AlignedLength;
875 
876         if(pssSegments[i].Length.QuadPart == 0)
877             DIE(("Virtual size of section %u is null\n", i));
878 
879         pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
880         pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
881 
882         /* ensure the memory image is no larger than 4GB */
883         nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
884         if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
885             DIE(("The image is too large\n"));
886     }
887 
888     if(nSectionAlignment >= PAGE_SIZE)
889         *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
890 
891     /* Success */
892     nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
893 
894 l_Return:
895     if(pBuffer)
896         ExFreePool(pBuffer);
897 
898     return nStatus;
899 }
900 
901 /*
902  * FUNCTION:  Waits in kernel mode indefinitely for a file object lock.
903  * ARGUMENTS: PFILE_OBJECT to wait for.
904  * RETURNS:   Status of the wait.
905  */
906 NTSTATUS
907 MmspWaitForFileLock(PFILE_OBJECT File)
908 {
909     return STATUS_SUCCESS;
910     //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
911 }
912 
913 
914 
915 VOID
916 NTAPI
917 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
918 {
919     ULONG Length;
920     LARGE_INTEGER Offset;
921     ULONG_PTR Entry;
922     SWAPENTRY SavedSwapEntry;
923     PFN_NUMBER Page;
924 
925     Page = 0;
926 
927     MmLockSectionSegment(Segment);
928 
929     Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
930     for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
931     {
932         Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
933         if (Entry)
934         {
935             MmSetPageEntrySectionSegment(Segment, &Offset, 0);
936             if (IS_SWAP_FROM_SSE(Entry))
937             {
938                 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
939             }
940             else
941             {
942                 Page = PFN_FROM_SSE(Entry);
943                 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
944                 if (SavedSwapEntry != 0)
945                 {
946                     MmSetSavedSwapEntryPage(Page, 0);
947                     MmFreeSwapPage(SavedSwapEntry);
948                 }
949                 MmReleasePageMemoryConsumer(MC_USER, Page);
950             }
951         }
952     }
953 
954     MmUnlockSectionSegment(Segment);
955 }
956 
957 static
958 VOID
959 NTAPI
960 FreeSegmentPage(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset)
961 {
962     ULONG_PTR Entry;
963     PFN_NUMBER Page;
964 
965     MmLockSectionSegment(Segment);
966 
967     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
968 
969     MmUnlockSectionSegment(Segment);
970 
971     /* This must be either a valid entry or nothing */
972     ASSERT(!IS_SWAP_FROM_SSE(Entry));
973 
974     /* There should be no reference anymore */
975     ASSERT(SHARE_COUNT_FROM_SSE(Entry) == 0);
976 
977     Page = PFN_FROM_SSE(Entry);
978     /* If there is a page, this must be because it's still dirty */
979     ASSERT(Page != 0);
980 
981     /* Write the page */
982     if (IS_DIRTY_SSE(Entry))
983         MiWritePage(Segment, Offset->QuadPart, Page);
984 
985     MmReleasePageMemoryConsumer(MC_USER, Page);
986 }
987 
988 _When_(OldIrql == MM_NOIRQL, _IRQL_requires_max_(DISPATCH_LEVEL))
989 _When_(OldIrql == MM_NOIRQL, _Requires_lock_not_held_(MmPfnLock))
990 _When_(OldIrql != MM_NOIRQL, _Requires_lock_held_(MmPfnLock))
991 _When_(OldIrql != MM_NOIRQL, _Releases_lock_(MmPfnLock))
992 _When_(OldIrql != MM_NOIRQL, _IRQL_requires_(DISPATCH_LEVEL))
993 VOID
994 NTAPI
995 MmDereferenceSegmentWithLock(
996     _Inout_ PMM_SECTION_SEGMENT Segment,
997     _In_ _When_(OldIrql != MM_NOIRQL, _IRQL_restores_) KIRQL OldIrql)
998 {
999     /* Lock the PFN lock because we mess around with SectionObjectPointers */
1000     if (OldIrql == MM_NOIRQL)
1001     {
1002         OldIrql = MiAcquirePfnLock();
1003     }
1004 
1005     if (InterlockedDecrement64(Segment->ReferenceCount) > 0)
1006     {
1007         /* Nothing to do yet */
1008         MiReleasePfnLock(OldIrql);
1009         return;
1010     }
1011 
1012     *Segment->Flags |= MM_SEGMENT_INDELETE;
1013 
1014     /* Flush the segment */
1015     if (*Segment->Flags & MM_DATAFILE_SEGMENT)
1016     {
1017         MiReleasePfnLock(OldIrql);
1018         /* Free the page table. This will flush any remaining dirty data */
1019         MmFreePageTablesSectionSegment(Segment, FreeSegmentPage);
1020 
1021         OldIrql = MiAcquirePfnLock();
1022         /* Delete the pointer on the file */
1023         ASSERT(Segment->FileObject->SectionObjectPointer->DataSectionObject == Segment);
1024         Segment->FileObject->SectionObjectPointer->DataSectionObject = NULL;
1025         MiReleasePfnLock(OldIrql);
1026         ObDereferenceObject(Segment->FileObject);
1027 
1028         ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
1029     }
1030     else
1031     {
1032         /* Most grotesque thing ever */
1033         PMM_IMAGE_SECTION_OBJECT ImageSectionObject = CONTAINING_RECORD(Segment->ReferenceCount, MM_IMAGE_SECTION_OBJECT, RefCount);
1034         PMM_SECTION_SEGMENT SectionSegments;
1035         ULONG NrSegments;
1036         ULONG i;
1037 
1038         /* Delete the pointer on the file */
1039         ASSERT(ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject == ImageSectionObject);
1040         ImageSectionObject->FileObject->SectionObjectPointer->ImageSectionObject = NULL;
1041         MiReleasePfnLock(OldIrql);
1042 
1043         ObDereferenceObject(ImageSectionObject->FileObject);
1044 
1045         NrSegments = ImageSectionObject->NrSegments;
1046         SectionSegments = ImageSectionObject->Segments;
1047         for (i = 0; i < NrSegments; i++)
1048         {
1049             if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
1050             {
1051                 MmpFreePageFileSegment(&SectionSegments[i]);
1052             }
1053 
1054             MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
1055         }
1056 
1057         ExFreePoolWithTag(ImageSectionObject->Segments, TAG_MM_SECTION_SEGMENT);
1058         ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
1059     }
1060 }
1061 
1062 VOID
1063 NTAPI
1064 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
1065                                PLARGE_INTEGER Offset)
1066 {
1067     ULONG_PTR Entry;
1068 
1069     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
1070     if (Entry == 0)
1071     {
1072         DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
1073         KeBugCheck(MEMORY_MANAGEMENT);
1074     }
1075     if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
1076     {
1077         DPRINT1("Maximum share count reached\n");
1078         KeBugCheck(MEMORY_MANAGEMENT);
1079     }
1080     if (IS_SWAP_FROM_SSE(Entry))
1081     {
1082         KeBugCheck(MEMORY_MANAGEMENT);
1083     }
1084     MmSetPageEntrySectionSegment(Segment, Offset, BUMPREF_SSE(Entry));
1085 }
1086 
1087 BOOLEAN
1088 NTAPI
1089 MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,
1090                                  PMM_SECTION_SEGMENT Segment,
1091                                  PLARGE_INTEGER Offset,
1092                                  BOOLEAN Dirty,
1093                                  BOOLEAN PageOut,
1094                                  ULONG_PTR *InEntry)
1095 {
1096     ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
1097     PFN_NUMBER Page = PFN_FROM_SSE(Entry);
1098     BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
1099     SWAPENTRY SwapEntry;
1100 
1101     if (Entry == 0)
1102     {
1103         DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
1104         KeBugCheck(MEMORY_MANAGEMENT);
1105     }
1106     if (SHARE_COUNT_FROM_SSE(Entry) == 0)
1107     {
1108         DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
1109         KeBugCheck(MEMORY_MANAGEMENT);
1110     }
1111     if (IS_SWAP_FROM_SSE(Entry))
1112     {
1113         KeBugCheck(MEMORY_MANAGEMENT);
1114     }
1115     Entry = DECREF_SSE(Entry);
1116     if (Dirty) Entry = DIRTY_SSE(Entry);
1117 
1118     /* If we are paging-out, pruning the page for real will be taken care of in MmCheckDirtySegment */
1119     if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
1120     {
1121         /* Update the page mapping in the segment and we're done */
1122         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1123         return FALSE;
1124     }
1125 
1126     /* We are pruning the last mapping on this page. See if we can keep it a bit more. */
1127     ASSERT(!PageOut);
1128 
1129     if (IsDataMap)
1130     {
1131         /* We can always keep memory in for data maps */
1132         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1133         return FALSE;
1134     }
1135 
1136     if (!FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
1137     {
1138         ASSERT(Segment->WriteCopy);
1139         ASSERT(!IS_DIRTY_SSE(Entry));
1140         ASSERT(MmGetSavedSwapEntryPage(Page) == 0);
1141         /* So this must have been a read-only page. Keep it ! */
1142         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1143         return FALSE;
1144     }
1145 
1146     /*
1147      * So this is a page for a shared section of a DLL.
1148      * We can keep it if it is not dirty.
1149      */
1150     SwapEntry = MmGetSavedSwapEntryPage(Page);
1151     if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
1152     {
1153         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1154         return FALSE;
1155     }
1156 
1157     /* No more processes are referencing this shared dirty page. Ditch it. */
1158     if (SwapEntry)
1159     {
1160         MmSetSavedSwapEntryPage(Page, 0);
1161         MmFreeSwapPage(SwapEntry);
1162     }
1163     MmSetPageEntrySectionSegment(Segment, Offset, 0);
1164     MmReleasePageMemoryConsumer(MC_USER, Page);
1165     return TRUE;
1166 }
1167 
1168 static
1169 NTSTATUS
1170 MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1171 {
1172     PEPROCESS Process;
1173     KIRQL Irql;
1174     PVOID DestAddress;
1175 
1176     Process = PsGetCurrentProcess();
1177     DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1178     if (DestAddress == NULL)
1179     {
1180         return STATUS_NO_MEMORY;
1181     }
1182     ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1183     ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1184     RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1185     MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1186     return STATUS_SUCCESS;
1187 }
1188 
1189 static
1190 NTSTATUS
1191 NTAPI
1192 MmMakeSegmentResident(
1193     _In_ PMM_SECTION_SEGMENT Segment,
1194     _In_ LONGLONG Offset,
1195     _In_ ULONG Length,
1196     _In_opt_ PLARGE_INTEGER ValidDataLength,
1197     _In_ BOOLEAN SetDirty)
1198 {
1199     /* Let's use a 64K granularity. */
1200     LONGLONG RangeStart, RangeEnd;
1201     NTSTATUS Status;
1202     PFILE_OBJECT FileObject = Segment->FileObject;
1203 
1204     /* Calculate our range, aligned on 64K if possible. */
1205     Status = RtlLongLongAdd(Offset, Length, &RangeEnd);
1206     ASSERT(NT_SUCCESS(Status));
1207     if (!NT_SUCCESS(Status))
1208         return Status;
1209 
1210     /* If the file is not random access and we are not the page out thread
1211      * read a 64K Chunk. */
1212     if (((ULONG_PTR)IoGetTopLevelIrp() != FSRTL_MOD_WRITE_TOP_LEVEL_IRP)
1213         && !FlagOn(FileObject->Flags, FO_RANDOM_ACCESS))
1214     {
1215         RangeStart = Offset - (Offset % _64K);
1216         if (RangeEnd % _64K)
1217             RangeEnd += _64K - (RangeEnd % _64K);
1218     }
1219     else
1220     {
1221         RangeStart = Offset  - (Offset % PAGE_SIZE);
1222         if (RangeEnd % PAGE_SIZE)
1223             RangeEnd += PAGE_SIZE - (RangeEnd % PAGE_SIZE);
1224     }
1225 
1226     /* Clamp if needed */
1227     if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
1228     {
1229         if (RangeEnd > Segment->RawLength.QuadPart)
1230             RangeEnd = Segment->RawLength.QuadPart;
1231     }
1232 
1233     /* Let's gooooooooo */
1234     for ( ; RangeStart < RangeEnd; RangeStart += _64K)
1235     {
1236         /* First take a look at where we miss pages */
1237         ULONG ToReadPageBits = 0;
1238         LONGLONG ChunkEnd = RangeStart + _64K;
1239 
1240         if (ChunkEnd > RangeEnd)
1241             ChunkEnd = RangeEnd;
1242 
1243         MmLockSectionSegment(Segment);
1244         for (LONGLONG ChunkOffset = RangeStart; ChunkOffset < ChunkEnd; ChunkOffset += PAGE_SIZE)
1245         {
1246             LARGE_INTEGER CurrentOffset;
1247 
1248             CurrentOffset.QuadPart = ChunkOffset;
1249             ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1250 
1251             /* Let any pending read proceed */
1252             while (MM_IS_WAIT_PTE(Entry))
1253             {
1254                 MmUnlockSectionSegment(Segment);
1255 
1256                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1257 
1258                 MmLockSectionSegment(Segment);
1259                 Entry = MmGetPageEntrySectionSegment(Segment, &CurrentOffset);
1260             }
1261 
1262             if (Entry != 0)
1263             {
1264                 /* Dirtify it if it's a resident page and we're asked to */
1265                 if (SetDirty && !IS_SWAP_FROM_SSE(Entry))
1266                     MmSetPageEntrySectionSegment(Segment, &CurrentOffset, DIRTY_SSE(Entry));
1267                 continue;
1268             }
1269 
1270             ToReadPageBits |= 1UL << ((ChunkOffset - RangeStart) >> PAGE_SHIFT);
1271 
1272             /* Put a wait entry here */
1273             MmSetPageEntrySectionSegment(Segment, &CurrentOffset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1274         }
1275         MmUnlockSectionSegment(Segment);
1276 
1277         if (ToReadPageBits == 0)
1278         {
1279             /* Nothing to do for this chunk */
1280             continue;
1281         }
1282 
1283         /* Now perform the actual read */
1284         LONGLONG ChunkOffset = RangeStart;
1285         while (ChunkOffset < ChunkEnd)
1286         {
1287             /* Move forward if there is a hole */
1288             ULONG BitSet;
1289             if (!_BitScanForward(&BitSet, ToReadPageBits))
1290             {
1291                 /* Nothing more to read */
1292                 break;
1293             }
1294             ToReadPageBits >>= BitSet;
1295             ChunkOffset += BitSet * PAGE_SIZE;
1296             ASSERT(ChunkOffset < ChunkEnd);
1297 
1298             /* Get the range we have to read */
1299             _BitScanForward(&BitSet, ~ToReadPageBits);
1300             ULONG ReadLength = BitSet * PAGE_SIZE;
1301 
1302             ASSERT(ReadLength <= _64K);
1303 
1304             /* Clamp (This is for image mappings */
1305             if ((ChunkOffset + ReadLength) > ChunkEnd)
1306                 ReadLength = ChunkEnd - ChunkOffset;
1307 
1308             ASSERT(ReadLength != 0);
1309 
1310             /* Allocate a MDL */
1311             PMDL Mdl = IoAllocateMdl(NULL, ReadLength, FALSE, FALSE, NULL);
1312             if (!Mdl)
1313             {
1314                 /* Damn. Roll-back. */
1315                 MmLockSectionSegment(Segment);
1316                 while (ChunkOffset < ChunkEnd)
1317                 {
1318                     if (ToReadPageBits & 1)
1319                     {
1320                         LARGE_INTEGER CurrentOffset;
1321                         CurrentOffset.QuadPart = ChunkOffset;
1322                         ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1323                         MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1324                     }
1325                     ToReadPageBits >>= 1;
1326                     ChunkOffset += PAGE_SIZE;
1327                 }
1328                 MmUnlockSectionSegment(Segment);
1329                 return STATUS_INSUFFICIENT_RESOURCES;
1330             }
1331 
1332             /* Get our pages */
1333             PPFN_NUMBER Pages = MmGetMdlPfnArray(Mdl);
1334             RtlZeroMemory(Pages, BYTES_TO_PAGES(ReadLength) * sizeof(PFN_NUMBER));
1335             for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1336             {
1337                 Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Pages[i]);
1338                 if (!NT_SUCCESS(Status))
1339                 {
1340                     /* Damn. Roll-back. */
1341                     for (UINT j = 0; j < i; j++)
1342                         MmReleasePageMemoryConsumer(MC_USER, Pages[j]);
1343                     goto Failed;
1344                 }
1345             }
1346 
1347             Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ;
1348 
1349             LARGE_INTEGER FileOffset;
1350             FileOffset.QuadPart = Segment->Image.FileOffset + ChunkOffset;
1351 
1352             /* Clamp to VDL */
1353             if (ValidDataLength && ((FileOffset.QuadPart + ReadLength) > ValidDataLength->QuadPart))
1354             {
1355                 if (FileOffset.QuadPart > ValidDataLength->QuadPart)
1356                 {
1357                     /* Great, nothing to read. */
1358                     goto AssignPagesToSegment;
1359                 }
1360 
1361                 Mdl->Size = (FileOffset.QuadPart + ReadLength) - ValidDataLength->QuadPart;
1362             }
1363 
1364             KEVENT Event;
1365             KeInitializeEvent(&Event, NotificationEvent, FALSE);
1366 
1367             /* Disable APCs */
1368             KIRQL OldIrql;
1369             KeRaiseIrql(APC_LEVEL, &OldIrql);
1370 
1371             IO_STATUS_BLOCK Iosb;
1372             Status = IoPageRead(FileObject, Mdl, &FileOffset, &Event, &Iosb);
1373             if (Status == STATUS_PENDING)
1374             {
1375                 KeWaitForSingleObject(&Event, WrPageIn, KernelMode, FALSE, NULL);
1376                 Status = Iosb.Status;
1377             }
1378 
1379             if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
1380             {
1381                 MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
1382             }
1383 
1384             KeLowerIrql(OldIrql);
1385 
1386             if (Status == STATUS_END_OF_FILE)
1387             {
1388                 DPRINT1("Got STATUS_END_OF_FILE at offset %I64d for file %wZ.\n", FileOffset.QuadPart, &FileObject->FileName);
1389                 Status = STATUS_SUCCESS;
1390             }
1391 
1392             if (!NT_SUCCESS(Status))
1393             {
1394                 /* Damn. Roll back. */
1395                 for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1396                     MmReleasePageMemoryConsumer(MC_USER, Pages[i]);
1397 
1398 Failed:
1399                 MmLockSectionSegment(Segment);
1400                 while (ChunkOffset < ChunkEnd)
1401                 {
1402                     if (ToReadPageBits & 1)
1403                     {
1404                         LARGE_INTEGER CurrentOffset;
1405                         CurrentOffset.QuadPart = ChunkOffset;
1406                         ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1407                         MmSetPageEntrySectionSegment(Segment, &CurrentOffset, 0);
1408                     }
1409                     ToReadPageBits >>= 1;
1410                     ChunkOffset += PAGE_SIZE;
1411                 }
1412                 MmUnlockSectionSegment(Segment);
1413                 IoFreeMdl(Mdl);;
1414                 return Status;
1415             }
1416 
1417 AssignPagesToSegment:
1418             MmLockSectionSegment(Segment);
1419 
1420             for (UINT i = 0; i < BYTES_TO_PAGES(ReadLength); i++)
1421             {
1422                 ULONG_PTR Entry = MAKE_SSE(Pages[i] << PAGE_SHIFT, 0);
1423                 LARGE_INTEGER CurrentOffset;
1424                 CurrentOffset.QuadPart = ChunkOffset + (i * PAGE_SIZE);
1425 
1426                 ASSERT(MM_IS_WAIT_PTE(MmGetPageEntrySectionSegment(Segment, &CurrentOffset)));
1427 
1428                 if (SetDirty)
1429                     Entry = DIRTY_SSE(Entry);
1430 
1431                 MmSetPageEntrySectionSegment(Segment, &CurrentOffset, Entry);
1432             }
1433 
1434             MmUnlockSectionSegment(Segment);
1435 
1436             IoFreeMdl(Mdl);
1437             ToReadPageBits >>= BitSet;
1438             ChunkOffset += BitSet * PAGE_SIZE;
1439         }
1440     }
1441 
1442     return STATUS_SUCCESS;
1443 }
1444 
1445 static VOID
1446 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
1447                       PVOID BaseAddress,
1448                       SIZE_T RegionSize,
1449                       ULONG OldType,
1450                       ULONG OldProtect,
1451                       ULONG NewType,
1452                       ULONG NewProtect)
1453 {
1454     PMEMORY_AREA MemoryArea;
1455     PMM_SECTION_SEGMENT Segment;
1456     BOOLEAN DoCOW = FALSE;
1457     ULONG i;
1458     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1459 
1460     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1461     ASSERT(MemoryArea != NULL);
1462     Segment = MemoryArea->SectionData.Segment;
1463     MmLockSectionSegment(Segment);
1464 
1465     if ((Segment->WriteCopy) &&
1466             (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1467     {
1468         DoCOW = TRUE;
1469     }
1470 
1471     if (OldProtect != NewProtect)
1472     {
1473         for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1474         {
1475             SWAPENTRY SwapEntry;
1476             PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1477             ULONG Protect = NewProtect;
1478 
1479             /* Wait for a wait entry to disappear */
1480             do
1481             {
1482                 MmGetPageFileMapping(Process, Address, &SwapEntry);
1483                 if (SwapEntry != MM_WAIT_ENTRY)
1484                     break;
1485                 MmUnlockSectionSegment(Segment);
1486                 MmUnlockAddressSpace(AddressSpace);
1487                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1488                 MmLockAddressSpace(AddressSpace);
1489                 MmLockSectionSegment(Segment);
1490             }
1491             while (TRUE);
1492 
1493             /*
1494              * If we doing COW for this segment then check if the page is
1495              * already private.
1496              */
1497             if (DoCOW && (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address)))
1498             {
1499                 LARGE_INTEGER Offset;
1500                 ULONG_PTR Entry;
1501                 PFN_NUMBER Page;
1502 
1503                 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1504                                   + MemoryArea->SectionData.ViewOffset;
1505                 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1506                 /*
1507                  * An MM_WAIT_ENTRY is ok in this case...  It'll just count as
1508                  * IS_SWAP_FROM_SSE and we'll do the right thing.
1509                  */
1510                 Page = MmGetPfnForProcess(Process, Address);
1511 
1512                 Protect = PAGE_READONLY;
1513                 if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
1514                 {
1515                     Protect = NewProtect;
1516                 }
1517             }
1518 
1519             if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
1520             {
1521                 MmSetPageProtect(Process, Address,
1522                                  Protect);
1523             }
1524         }
1525     }
1526 
1527     MmUnlockSectionSegment(Segment);
1528 }
1529 
1530 NTSTATUS
1531 NTAPI
1532 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1533                              MEMORY_AREA* MemoryArea,
1534                              PVOID Address,
1535                              BOOLEAN Locked)
1536 {
1537     LARGE_INTEGER Offset;
1538     PFN_NUMBER Page;
1539     NTSTATUS Status;
1540     PMM_SECTION_SEGMENT Segment;
1541     ULONG_PTR Entry;
1542     ULONG_PTR Entry1;
1543     ULONG Attributes;
1544     PMM_REGION Region;
1545     BOOLEAN HasSwapEntry;
1546     PVOID PAddress;
1547     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1548     SWAPENTRY SwapEntry;
1549 
1550     ASSERT(Locked);
1551 
1552     /*
1553      * There is a window between taking the page fault and locking the
1554      * address space when another thread could load the page so we check
1555      * that.
1556      */
1557     if (MmIsPagePresent(Process, Address))
1558     {
1559         return STATUS_SUCCESS;
1560     }
1561 
1562     if (MmIsDisabledPage(Process, Address))
1563     {
1564         return STATUS_ACCESS_VIOLATION;
1565     }
1566 
1567     /*
1568      * Check for the virtual memory area being deleted.
1569      */
1570     if (MemoryArea->DeleteInProgress)
1571     {
1572         return STATUS_UNSUCCESSFUL;
1573     }
1574 
1575     PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1576     Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1577                       + MemoryArea->SectionData.ViewOffset;
1578 
1579     Segment = MemoryArea->SectionData.Segment;
1580     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1581                           &MemoryArea->SectionData.RegionListHead,
1582                           Address, NULL);
1583     ASSERT(Region != NULL);
1584 
1585     /* Check for a NOACCESS mapping */
1586     if (Region->Protect & PAGE_NOACCESS)
1587     {
1588         return STATUS_ACCESS_VIOLATION;
1589     }
1590 
1591     if (Region->Protect & PAGE_GUARD)
1592     {
1593         /* Remove it */
1594         Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1595                 &MemoryArea->SectionData.RegionListHead,
1596                 Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1597                 MmAlterViewAttributes);
1598 
1599         if (!NT_SUCCESS(Status))
1600         {
1601             DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1602         }
1603 
1604         return STATUS_GUARD_PAGE_VIOLATION;
1605     }
1606 
1607     HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1608 
1609     /* See if we should use a private page */
1610     if (HasSwapEntry)
1611     {
1612         SWAPENTRY DummyEntry;
1613 
1614         MmGetPageFileMapping(Process, Address, &SwapEntry);
1615         if (SwapEntry == MM_WAIT_ENTRY)
1616         {
1617             MmUnlockAddressSpace(AddressSpace);
1618             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1619             MmLockAddressSpace(AddressSpace);
1620             return STATUS_MM_RESTART_OPERATION;
1621         }
1622 
1623         MI_SET_USAGE(MI_USAGE_SECTION);
1624         if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1625         if (!Process) MI_SET_PROCESS2("Kernel Section");
1626         Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1627         if (!NT_SUCCESS(Status))
1628         {
1629             return STATUS_NO_MEMORY;
1630         }
1631 
1632         /*
1633          * Must be private page we have swapped out.
1634          */
1635 
1636         /*
1637         * Sanity check
1638         */
1639         MmDeletePageFileMapping(Process, Address, &DummyEntry);
1640         ASSERT(DummyEntry == SwapEntry);
1641 
1642         /* Tell everyone else we are serving the fault. */
1643         MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1644 
1645         MmUnlockAddressSpace(AddressSpace);
1646 
1647         Status = MmReadFromSwapPage(SwapEntry, Page);
1648         if (!NT_SUCCESS(Status))
1649         {
1650             DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1651             KeBugCheck(MEMORY_MANAGEMENT);
1652         }
1653 
1654         MmLockAddressSpace(AddressSpace);
1655         MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1656         ASSERT(DummyEntry == MM_WAIT_ENTRY);
1657 
1658         Status = MmCreateVirtualMapping(Process,
1659                                         PAddress,
1660                                         Region->Protect,
1661                                         Page);
1662         if (!NT_SUCCESS(Status))
1663         {
1664             DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1665             KeBugCheck(MEMORY_MANAGEMENT);
1666             return Status;
1667         }
1668 
1669         /*
1670          * Store the swap entry for later use.
1671          */
1672         MmSetSavedSwapEntryPage(Page, SwapEntry);
1673 
1674         /*
1675          * Add the page to the process's working set
1676          */
1677         if (Process) MmInsertRmap(Page, Process, Address);
1678         /*
1679          * Finish the operation
1680          */
1681         DPRINT("Address 0x%p\n", Address);
1682         return STATUS_SUCCESS;
1683     }
1684 
1685     /*
1686      * Lock the segment
1687      */
1688     MmLockSectionSegment(Segment);
1689 
1690     /*
1691      * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1692      */
1693     if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
1694     {
1695         MmUnlockSectionSegment(Segment);
1696         /*
1697          * Just map the desired physical page
1698          */
1699         Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1700         Status = MmCreatePhysicalMapping(Process,
1701                                          PAddress,
1702                                          Region->Protect,
1703                                          Page);
1704         if (!NT_SUCCESS(Status))
1705         {
1706             DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1707             KeBugCheck(MEMORY_MANAGEMENT);
1708             return Status;
1709         }
1710 
1711         /*
1712          * Cleanup and release locks
1713          */
1714         DPRINT("Address 0x%p\n", Address);
1715         return STATUS_SUCCESS;
1716     }
1717 
1718     /*
1719      * Check if this page needs to be mapped COW
1720      */
1721     if ((Segment->WriteCopy) &&
1722         (Region->Protect == PAGE_READWRITE || Region->Protect == PAGE_EXECUTE_READWRITE))
1723     {
1724         Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1725     }
1726     else
1727     {
1728         Attributes = Region->Protect;
1729     }
1730 
1731 
1732     /*
1733      * Get the entry corresponding to the offset within the section
1734      */
1735     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1736     if (Entry == 0)
1737     {
1738         /*
1739          * If the entry is zero, then we need to load the page.
1740          */
1741         if ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart)) && (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap))
1742         {
1743             /* We are beyond the data which is on file. Just get a new page. */
1744             MI_SET_USAGE(MI_USAGE_SECTION);
1745             if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1746             if (!Process) MI_SET_PROCESS2("Kernel Section");
1747             Status = MmRequestPageMemoryConsumer(MC_USER, FALSE, &Page);
1748             if (!NT_SUCCESS(Status))
1749             {
1750                 MmUnlockSectionSegment(Segment);
1751                 return STATUS_NO_MEMORY;
1752             }
1753             MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SSE(Page << PAGE_SHIFT, 1));
1754             MmUnlockSectionSegment(Segment);
1755 
1756             Status = MmCreateVirtualMapping(Process, PAddress, Attributes, Page);
1757             if (!NT_SUCCESS(Status))
1758             {
1759                 DPRINT1("Unable to create virtual mapping\n");
1760                 KeBugCheck(MEMORY_MANAGEMENT);
1761             }
1762             ASSERT(MmIsPagePresent(Process, PAddress));
1763             if (Process)
1764                 MmInsertRmap(Page, Process, Address);
1765 
1766             DPRINT("Address 0x%p\n", Address);
1767             return STATUS_SUCCESS;
1768         }
1769 
1770         MmUnlockSectionSegment(Segment);
1771         MmUnlockAddressSpace(AddressSpace);
1772 
1773         /* The data must be paged in. Lock the file, so that the VDL doesn't get updated behind us. */
1774         FsRtlAcquireFileExclusive(Segment->FileObject);
1775 
1776         PFSRTL_COMMON_FCB_HEADER FcbHeader = Segment->FileObject->FsContext;
1777 
1778         Status = MmMakeSegmentResident(Segment, Offset.QuadPart, PAGE_SIZE, &FcbHeader->ValidDataLength, FALSE);
1779 
1780         FsRtlReleaseFile(Segment->FileObject);
1781 
1782         /* Lock address space again */
1783         MmLockAddressSpace(AddressSpace);
1784         if (!NT_SUCCESS(Status))
1785         {
1786             if (Status == STATUS_NO_MEMORY)
1787             {
1788                 return Status;
1789             }
1790             /* Damn */
1791             DPRINT1("Failed to page data in!\n");
1792             return STATUS_IN_PAGE_ERROR;
1793         }
1794 
1795         /* Everything went fine. Restart the operation */
1796         return STATUS_MM_RESTART_OPERATION;
1797     }
1798     else if (IS_SWAP_FROM_SSE(Entry))
1799     {
1800         SWAPENTRY SwapEntry;
1801 
1802         SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1803 
1804         /* See if a page op is running on this segment. */
1805         if (SwapEntry == MM_WAIT_ENTRY)
1806         {
1807             MmUnlockSectionSegment(Segment);
1808             MmUnlockAddressSpace(AddressSpace);
1809             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
1810             MmLockAddressSpace(AddressSpace);
1811             return STATUS_MM_RESTART_OPERATION;
1812         }
1813 
1814         Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1815         if (!NT_SUCCESS(Status))
1816         {
1817             MmUnlockSectionSegment(Segment);
1818             return STATUS_NO_MEMORY;
1819         }
1820 
1821         /*
1822         * Release all our locks and read in the page from disk
1823         */
1824         MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1825         MmUnlockSectionSegment(Segment);
1826 
1827         MmUnlockAddressSpace(AddressSpace);
1828 
1829         Status = MmReadFromSwapPage(SwapEntry, Page);
1830         if (!NT_SUCCESS(Status))
1831         {
1832             KeBugCheck(MEMORY_MANAGEMENT);
1833         }
1834 
1835         /*
1836          * Relock the address space and segment
1837          */
1838         MmLockAddressSpace(AddressSpace);
1839         MmLockSectionSegment(Segment);
1840 
1841         /*
1842          * Check the entry. No one should change the status of a page
1843          * that has a pending page-in.
1844          */
1845         Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
1846         if (Entry1 != MAKE_SWAP_SSE(MM_WAIT_ENTRY))
1847         {
1848             DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1849             KeBugCheck(MEMORY_MANAGEMENT);
1850         }
1851 
1852         /*
1853          * Save the swap entry.
1854          */
1855         MmSetSavedSwapEntryPage(Page, SwapEntry);
1856 
1857         /* Map the page into the process address space */
1858         Status = MmCreateVirtualMapping(Process,
1859                                         PAddress,
1860                                         Attributes,
1861                                         Page);
1862         if (!NT_SUCCESS(Status))
1863         {
1864             DPRINT1("Unable to create virtual mapping\n");
1865             KeBugCheck(MEMORY_MANAGEMENT);
1866         }
1867         if (Process)
1868             MmInsertRmap(Page, Process, Address);
1869 
1870         /*
1871          * Mark the offset within the section as having valid, in-memory
1872          * data
1873          */
1874         Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1875         MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1876         MmUnlockSectionSegment(Segment);
1877 
1878         DPRINT("Address 0x%p\n", Address);
1879         return STATUS_SUCCESS;
1880     }
1881     else
1882     {
1883         /* We already have a page on this section offset. Map it into the process address space. */
1884         Page = PFN_FROM_SSE(Entry);
1885 
1886         Status = MmCreateVirtualMapping(Process,
1887                                         PAddress,
1888                                         Attributes,
1889                                         Page);
1890         if (!NT_SUCCESS(Status))
1891         {
1892             DPRINT1("Unable to create virtual mapping\n");
1893             KeBugCheck(MEMORY_MANAGEMENT);
1894         }
1895 
1896         if (Process)
1897             MmInsertRmap(Page, Process, Address);
1898 
1899         /* Take a reference on it */
1900         MmSharePageEntrySectionSegment(Segment, &Offset);
1901         MmUnlockSectionSegment(Segment);
1902 
1903         DPRINT("Address 0x%p\n", Address);
1904         return STATUS_SUCCESS;
1905     }
1906 }
1907 
1908 NTSTATUS
1909 NTAPI
1910 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1911                          MEMORY_AREA* MemoryArea,
1912                          PVOID Address,
1913                          BOOLEAN Locked)
1914 {
1915     PMM_SECTION_SEGMENT Segment;
1916     PFN_NUMBER OldPage;
1917     PFN_NUMBER NewPage;
1918     PFN_NUMBER UnmappedPage;
1919     PVOID PAddress;
1920     LARGE_INTEGER Offset;
1921     PMM_REGION Region;
1922     ULONG_PTR Entry;
1923     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1924     BOOLEAN Cow = FALSE;
1925     ULONG NewProtect;
1926     BOOLEAN Unmapped;
1927     NTSTATUS Status;
1928 
1929     DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1930 
1931     /* Get the region for this address */
1932     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1933                         &MemoryArea->SectionData.RegionListHead,
1934                         Address, NULL);
1935     ASSERT(Region != NULL);
1936     if (!(Region->Protect & PAGE_IS_WRITABLE))
1937         return STATUS_ACCESS_VIOLATION;
1938 
1939     /* Make sure we have a page mapping for this address.  */
1940     if (!MmIsPagePresent(Process, Address))
1941     {
1942         NTSTATUS Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, Locked);
1943         if (!NT_SUCCESS(Status))
1944         {
1945             /* This is invalid access ! */
1946             return Status;
1947         }
1948     }
1949 
1950     /*
1951      * Check if the page has already been set readwrite
1952      */
1953     if (MmGetPageProtect(Process, Address) & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE))
1954     {
1955         DPRINT("Address 0x%p\n", Address);
1956         return STATUS_SUCCESS;
1957     }
1958 
1959     /* Check if we are doing Copy-On-Write */
1960     Segment = MemoryArea->SectionData.Segment;
1961     Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1962 
1963     if (!Cow)
1964     {
1965         /* Simply update page protection and we're done */
1966         MmSetPageProtect(Process, Address, Region->Protect);
1967         return STATUS_SUCCESS;
1968     }
1969 
1970     /* Calculate the new protection & check if we should update the region */
1971     NewProtect = Region->Protect;
1972     if (NewProtect & PAGE_IS_WRITECOPY)
1973     {
1974         NewProtect &= ~PAGE_IS_WRITECOPY;
1975         if (Region->Protect & PAGE_IS_EXECUTABLE)
1976             NewProtect |= PAGE_EXECUTE_READWRITE;
1977         else
1978             NewProtect |= PAGE_READWRITE;
1979         MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1980                 &MemoryArea->SectionData.RegionListHead,
1981                 Address, PAGE_SIZE, Region->Type, NewProtect,
1982                 MmAlterViewAttributes);
1983     }
1984 
1985     /*
1986      * Find the offset of the page
1987      */
1988     PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1989     Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1990                       + MemoryArea->SectionData.ViewOffset;
1991 
1992     /* Get the page mapping this section offset. */
1993     MmLockSectionSegment(Segment);
1994     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1995 
1996     /* Get the current page mapping for the process */
1997     ASSERT(MmIsPagePresent(Process, PAddress));
1998     OldPage = MmGetPfnForProcess(Process, PAddress);
1999     ASSERT(OldPage != 0);
2000 
2001     if (IS_SWAP_FROM_SSE(Entry) ||
2002             PFN_FROM_SSE(Entry) != OldPage)
2003     {
2004         MmUnlockSectionSegment(Segment);
2005         /* This is a private page. We must only change the page protection. */
2006         MmSetPageProtect(Process, PAddress, NewProtect);
2007         return STATUS_SUCCESS;
2008     }
2009 
2010     /*
2011      * Allocate a page
2012      */
2013     Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
2014     if (!NT_SUCCESS(Status))
2015     {
2016         MmUnlockSectionSegment(Segment);
2017         return STATUS_NO_MEMORY;
2018     }
2019 
2020     /*
2021      * Copy the old page
2022      */
2023     NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2024 
2025     /*
2026      * Unshare the old page.
2027      */
2028     DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2029     Unmapped = MmDeleteVirtualMapping(Process, PAddress, NULL, &UnmappedPage);
2030     if (!Unmapped || (UnmappedPage != OldPage))
2031     {
2032         /* Uh , we had a page just before, but suddenly it changes. Someone corrupted us. */
2033         KeBugCheckEx(MEMORY_MANAGEMENT,
2034                     (ULONG_PTR)Process,
2035                     (ULONG_PTR)PAddress,
2036                     (ULONG_PTR)__FILE__,
2037                     __LINE__);
2038     }
2039 
2040     if (Process)
2041         MmDeleteRmap(OldPage, Process, PAddress);
2042     MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, FALSE, FALSE, NULL);
2043     MmUnlockSectionSegment(Segment);
2044 
2045     /*
2046      * Set the PTE to point to the new page
2047      */
2048     if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2049     {
2050         DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2051         KeBugCheck(MEMORY_MANAGEMENT);
2052     }
2053 
2054     if (Process)
2055         MmInsertRmap(NewPage, Process, PAddress);
2056 
2057     DPRINT("Address 0x%p\n", Address);
2058     return STATUS_SUCCESS;
2059 }
2060 
2061 NTSTATUS
2062 NTAPI
2063 MmProtectSectionView(PMMSUPPORT AddressSpace,
2064                      PMEMORY_AREA MemoryArea,
2065                      PVOID BaseAddress,
2066                      SIZE_T Length,
2067                      ULONG Protect,
2068                      PULONG OldProtect)
2069 {
2070     PMM_REGION Region;
2071     NTSTATUS Status;
2072     ULONG_PTR MaxLength;
2073 
2074     MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2075     if (Length > MaxLength)
2076         Length = (ULONG)MaxLength;
2077 
2078     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2079                           &MemoryArea->SectionData.RegionListHead,
2080                           BaseAddress, NULL);
2081     ASSERT(Region != NULL);
2082 
2083     if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2084             Region->Protect != Protect)
2085     {
2086         return STATUS_INVALID_PAGE_PROTECTION;
2087     }
2088 
2089     *OldProtect = Region->Protect;
2090     Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2091                            &MemoryArea->SectionData.RegionListHead,
2092                            BaseAddress, Length, Region->Type, Protect,
2093                            MmAlterViewAttributes);
2094 
2095     return Status;
2096 }
2097 
2098 NTSTATUS NTAPI
2099 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2100                    PVOID Address,
2101                    PMEMORY_BASIC_INFORMATION Info,
2102                    PSIZE_T ResultLength)
2103 {
2104     PMM_REGION Region;
2105     PVOID RegionBaseAddress;
2106     PMM_SECTION_SEGMENT Segment;
2107 
2108     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2109                           &MemoryArea->SectionData.RegionListHead,
2110                           Address, &RegionBaseAddress);
2111     if (Region == NULL)
2112     {
2113         return STATUS_UNSUCCESSFUL;
2114     }
2115 
2116     if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
2117     {
2118         Segment = MemoryArea->SectionData.Segment;
2119         Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2120         Info->Type = MEM_IMAGE;
2121     }
2122     else
2123     {
2124         Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2125         Info->Type = MEM_MAPPED;
2126     }
2127     Info->BaseAddress = RegionBaseAddress;
2128     Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
2129     Info->RegionSize = Region->Length;
2130     Info->State = MEM_COMMIT;
2131     Info->Protect = Region->Protect;
2132 
2133     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2134     return STATUS_SUCCESS;
2135 }
2136 
2137 VOID NTAPI
2138 MmpDeleteSection(PVOID ObjectBody)
2139 {
2140     PSECTION Section = ObjectBody;
2141 
2142     /* Check if it's an ARM3, or ReactOS section */
2143     if (!MiIsRosSectionObject(Section))
2144     {
2145         MiDeleteARM3Section(ObjectBody);
2146         return;
2147     }
2148 
2149     DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2150     if (Section->u.Flags.Image)
2151     {
2152         PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2153 
2154         /*
2155          * NOTE: Section->ImageSection can be NULL for short time
2156          * during the section creating. If we fail for some reason
2157          * until the image section is properly initialized we shouldn't
2158          * process further here.
2159          */
2160         if (Section->Segment == NULL)
2161             return;
2162 
2163         KIRQL OldIrql = MiAcquirePfnLock();
2164         ImageSectionObject->SectionCount--;
2165 
2166         /* We just dereference the first segment */
2167         ASSERT(ImageSectionObject->RefCount > 0);
2168         /* MmDereferenceSegmentWithLock releases PFN lock */
2169         MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2170     }
2171     else
2172     {
2173         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
2174 
2175         /*
2176          * NOTE: Section->Segment can be NULL for short time
2177          * during the section creating.
2178          */
2179         if (Segment == NULL)
2180             return;
2181 
2182         KIRQL OldIrql = MiAcquirePfnLock();
2183         Segment->SectionCount--;
2184 
2185         /* MmDereferenceSegmentWithLock releases PFN lock */
2186         MmDereferenceSegmentWithLock(Segment, OldIrql);
2187     }
2188 }
2189 
2190 VOID NTAPI
2191 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2192                 IN PVOID Object,
2193                 IN ACCESS_MASK GrantedAccess,
2194                 IN ULONG ProcessHandleCount,
2195                 IN ULONG SystemHandleCount)
2196 {
2197     DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2198 }
2199 
2200 CODE_SEG("INIT")
2201 NTSTATUS
2202 NTAPI
2203 MmCreatePhysicalMemorySection(VOID)
2204 {
2205     PSECTION PhysSection;
2206     NTSTATUS Status;
2207     OBJECT_ATTRIBUTES Obj;
2208     UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2209     LARGE_INTEGER SectionSize;
2210     HANDLE Handle;
2211     PMM_SECTION_SEGMENT Segment;
2212 
2213     /*
2214      * Create the section mapping physical memory
2215      */
2216     SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
2217     InitializeObjectAttributes(&Obj,
2218                                &Name,
2219                                OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
2220                                NULL,
2221                                NULL);
2222     /*
2223      * Create the Object
2224      */
2225     Status = ObCreateObject(KernelMode,
2226                             MmSectionObjectType,
2227                             &Obj,
2228                             ExGetPreviousMode(),
2229                             NULL,
2230                             sizeof(*PhysSection),
2231                             0,
2232                             0,
2233                             (PVOID*)&PhysSection);
2234     if (!NT_SUCCESS(Status))
2235     {
2236         DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2237         return Status;
2238     }
2239 
2240     /*
2241      * Initialize it
2242      */
2243     RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2244 
2245     /* Mark this as a "ROS Section" */
2246     PhysSection->u.Flags.filler = 1;
2247     PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
2248     PhysSection->u.Flags.PhysicalMemory = 1;
2249     PhysSection->SizeOfSection = SectionSize;
2250     Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2251                                     TAG_MM_SECTION_SEGMENT);
2252     if (Segment == NULL)
2253     {
2254         ObDereferenceObject(PhysSection);
2255         return STATUS_NO_MEMORY;
2256     }
2257     RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2258     PhysSection->Segment = (PSEGMENT)Segment;
2259     Segment->RefCount = 1;
2260 
2261     Segment->ReferenceCount = &Segment->RefCount;
2262     Segment->Flags = &Segment->SegFlags;
2263 
2264     ExInitializeFastMutex(&Segment->Lock);
2265     Segment->Image.FileOffset = 0;
2266     Segment->Protection = PAGE_EXECUTE_READWRITE;
2267     Segment->RawLength = SectionSize;
2268     Segment->Length = SectionSize;
2269     Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
2270     Segment->WriteCopy = FALSE;
2271     Segment->Image.VirtualAddress = 0;
2272     Segment->Image.Characteristics = 0;
2273     MiInitializeSectionPageTable(Segment);
2274 
2275     Status = ObInsertObject(PhysSection,
2276                             NULL,
2277                             SECTION_ALL_ACCESS,
2278                             0,
2279                             NULL,
2280                             &Handle);
2281     if (!NT_SUCCESS(Status))
2282     {
2283         ObDereferenceObject(PhysSection);
2284         return Status;
2285     }
2286     ObCloseHandle(Handle, KernelMode);
2287 
2288     return STATUS_SUCCESS;
2289 }
2290 
2291 CODE_SEG("INIT")
2292 NTSTATUS
2293 NTAPI
2294 MmInitSectionImplementation(VOID)
2295 {
2296     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2297     UNICODE_STRING Name;
2298 
2299     DPRINT("Creating Section Object Type\n");
2300 
2301     /* Initialize the section based root */
2302     ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2303     MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2304 
2305     /* Initialize the Section object type  */
2306     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2307     RtlInitUnicodeString(&Name, L"Section");
2308     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2309     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2310     ObjectTypeInitializer.PoolType = PagedPool;
2311     ObjectTypeInitializer.UseDefaultObject = TRUE;
2312     ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2313     ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2314     ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2315     ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2316     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2317     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2318 
2319     MmCreatePhysicalMemorySection();
2320 
2321     return STATUS_SUCCESS;
2322 }
2323 
2324 static
2325 NTSTATUS
2326 NTAPI
2327 MmCreateDataFileSection(PSECTION *SectionObject,
2328                         ACCESS_MASK DesiredAccess,
2329                         POBJECT_ATTRIBUTES ObjectAttributes,
2330                         PLARGE_INTEGER UMaximumSize,
2331                         ULONG SectionPageProtection,
2332                         ULONG AllocationAttributes,
2333                         PFILE_OBJECT FileObject,
2334                         BOOLEAN GotFileHandle)
2335 /*
2336  * Create a section backed by a data file
2337  */
2338 {
2339     PSECTION Section;
2340     NTSTATUS Status;
2341     LARGE_INTEGER MaximumSize;
2342     PMM_SECTION_SEGMENT Segment;
2343     KIRQL OldIrql;
2344 
2345     /*
2346      * Create the section
2347      */
2348     Status = ObCreateObject(ExGetPreviousMode(),
2349                             MmSectionObjectType,
2350                             ObjectAttributes,
2351                             ExGetPreviousMode(),
2352                             NULL,
2353                             sizeof(*Section),
2354                             0,
2355                             0,
2356                             (PVOID*)&Section);
2357     if (!NT_SUCCESS(Status))
2358     {
2359         return Status;
2360     }
2361     /*
2362      * Initialize it
2363      */
2364     RtlZeroMemory(Section, sizeof(*Section));
2365 
2366     /* Mark this as a "ROS" section */
2367     Section->u.Flags.filler = 1;
2368     Section->InitialPageProtection = SectionPageProtection;
2369     Section->u.Flags.File = 1;
2370 
2371     if (AllocationAttributes & SEC_NO_CHANGE)
2372         Section->u.Flags.NoChange = 1;
2373     if (AllocationAttributes & SEC_RESERVE)
2374         Section->u.Flags.Reserve = 1;
2375 
2376     if (!GotFileHandle)
2377     {
2378         ASSERT(UMaximumSize != NULL);
2379         // ASSERT(UMaximumSize->QuadPart != 0);
2380         MaximumSize = *UMaximumSize;
2381     }
2382     else
2383     {
2384         LARGE_INTEGER FileSize;
2385         Status = FsRtlGetFileSize(FileObject, &FileSize);
2386         if (!NT_SUCCESS(Status))
2387         {
2388             ObDereferenceObject(Section);
2389             return Status;
2390         }
2391 
2392         /*
2393          * FIXME: Revise this once a locking order for file size changes is
2394          * decided
2395          */
2396         if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2397         {
2398             MaximumSize = *UMaximumSize;
2399         }
2400         else
2401         {
2402             MaximumSize = FileSize;
2403             /* Mapping zero-sized files isn't allowed. */
2404             if (MaximumSize.QuadPart == 0)
2405             {
2406                 ObDereferenceObject(Section);
2407                 return STATUS_MAPPED_FILE_SIZE_ZERO;
2408             }
2409         }
2410 
2411         if (MaximumSize.QuadPart > FileSize.QuadPart)
2412         {
2413             Status = IoSetInformation(FileObject,
2414                                       FileEndOfFileInformation,
2415                                       sizeof(LARGE_INTEGER),
2416                                       &MaximumSize);
2417             if (!NT_SUCCESS(Status))
2418             {
2419                 ObDereferenceObject(Section);
2420                 return STATUS_SECTION_NOT_EXTENDED;
2421             }
2422         }
2423     }
2424 
2425     if (FileObject->SectionObjectPointer == NULL)
2426     {
2427         ObDereferenceObject(Section);
2428         return STATUS_INVALID_FILE_FOR_SECTION;
2429     }
2430 
2431     /*
2432      * Lock the file
2433      */
2434     Status = MmspWaitForFileLock(FileObject);
2435     if (Status != STATUS_SUCCESS)
2436     {
2437         ObDereferenceObject(Section);
2438         return Status;
2439     }
2440 
2441     /* Lock the PFN lock while messing with Section Object pointers */
2442 grab_segment:
2443     OldIrql = MiAcquirePfnLock();
2444     Segment = FileObject->SectionObjectPointer->DataSectionObject;
2445 
2446     while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2447     {
2448         MiReleasePfnLock(OldIrql);
2449         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
2450         OldIrql = MiAcquirePfnLock();
2451         Segment = FileObject->SectionObjectPointer->DataSectionObject;
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 (Segment == NULL)
2459     {
2460         /* Release the lock. ExAllocatePoolWithTag might acquire it */
2461         MiReleasePfnLock(OldIrql);
2462 
2463         Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2464                                         TAG_MM_SECTION_SEGMENT);
2465         if (Segment == NULL)
2466         {
2467             //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2468             ObDereferenceObject(Section);
2469             return STATUS_NO_MEMORY;
2470         }
2471 
2472         /* We are creating it */
2473         RtlZeroMemory(Segment, sizeof(*Segment));
2474         Segment->SegFlags = MM_DATAFILE_SEGMENT | MM_SEGMENT_INCREATE;
2475         Segment->RefCount = 1;
2476 
2477         /* Acquire lock again */
2478         OldIrql = MiAcquirePfnLock();
2479 
2480         if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2481         {
2482             /* Well that's bad luck. Restart it all over */
2483             MiReleasePfnLock(OldIrql);
2484             ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
2485             goto grab_segment;
2486         }
2487 
2488         FileObject->SectionObjectPointer->DataSectionObject = Segment;
2489 
2490         /* We're safe to release the lock now */
2491         MiReleasePfnLock(OldIrql);
2492 
2493         Section->Segment = (PSEGMENT)Segment;
2494 
2495         /* Self-referencing segment */
2496         Segment->Flags = &Segment->SegFlags;
2497         Segment->ReferenceCount = &Segment->RefCount;
2498 
2499         Segment->SectionCount = 1;
2500 
2501         ExInitializeFastMutex(&Segment->Lock);
2502         Segment->FileObject = FileObject;
2503         ObReferenceObject(FileObject);
2504 
2505         Segment->Image.FileOffset = 0;
2506         Segment->Protection = SectionPageProtection;
2507 
2508         Segment->Image.Characteristics = 0;
2509         Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
2510         if (AllocationAttributes & SEC_RESERVE)
2511         {
2512             Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2513         }
2514         else
2515         {
2516             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2517             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2518         }
2519         Segment->Image.VirtualAddress = 0;
2520         MiInitializeSectionPageTable(Segment);
2521 
2522         /* We're good to use it now */
2523         OldIrql = MiAcquirePfnLock();
2524         Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2525         MiReleasePfnLock(OldIrql);
2526     }
2527     else
2528     {
2529         Section->Segment = (PSEGMENT)Segment;
2530         InterlockedIncrement64(&Segment->RefCount);
2531         InterlockedIncrementUL(&Segment->SectionCount);
2532 
2533         MiReleasePfnLock(OldIrql);
2534 
2535         MmLockSectionSegment(Segment);
2536 
2537         if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2538                 !(AllocationAttributes & SEC_RESERVE))
2539         {
2540             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2541             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2542         }
2543 
2544         MmUnlockSectionSegment(Segment);
2545     }
2546     Section->SizeOfSection = MaximumSize;
2547 
2548     //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2549     *SectionObject = Section;
2550     return STATUS_SUCCESS;
2551 }
2552 
2553 /*
2554  TODO: not that great (declaring loaders statically, having to declare all of
2555  them, having to keep them extern, etc.), will fix in the future
2556 */
2557 extern NTSTATUS NTAPI PeFmtCreateSection
2558 (
2559     IN CONST VOID * FileHeader,
2560     IN SIZE_T FileHeaderSize,
2561     IN PVOID File,
2562     OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2563     OUT PULONG Flags,
2564     IN PEXEFMT_CB_READ_FILE ReadFileCb,
2565     IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2566 );
2567 
2568 extern NTSTATUS NTAPI ElfFmtCreateSection
2569 (
2570     IN CONST VOID * FileHeader,
2571     IN SIZE_T FileHeaderSize,
2572     IN PVOID File,
2573     OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2574     OUT PULONG Flags,
2575     IN PEXEFMT_CB_READ_FILE ReadFileCb,
2576     IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2577 );
2578 
2579 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2580 {
2581     PeFmtCreateSection,
2582 #ifdef __ELF
2583     ElfFmtCreateSection
2584 #endif
2585 };
2586 
2587 static
2588 PMM_SECTION_SEGMENT
2589 NTAPI
2590 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2591 {
2592     SIZE_T SizeOfSegments;
2593     PMM_SECTION_SEGMENT Segments;
2594 
2595     /* TODO: check for integer overflow */
2596     SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2597 
2598     Segments = ExAllocatePoolWithTag(NonPagedPool,
2599                                      SizeOfSegments,
2600                                      TAG_MM_SECTION_SEGMENT);
2601 
2602     if(Segments)
2603         RtlZeroMemory(Segments, SizeOfSegments);
2604 
2605     return Segments;
2606 }
2607 static
2608 NTSTATUS
2609 NTAPI
2610 ExeFmtpReadFile(IN PVOID File,
2611                 IN PLARGE_INTEGER Offset,
2612                 IN ULONG Length,
2613                 OUT PVOID * Data,
2614                 OUT PVOID * AllocBase,
2615                 OUT PULONG ReadSize)
2616 {
2617     NTSTATUS Status;
2618     LARGE_INTEGER FileOffset;
2619     ULONG AdjustOffset;
2620     ULONG OffsetAdjustment;
2621     ULONG BufferSize;
2622     ULONG UsedSize;
2623     PVOID Buffer;
2624     PFILE_OBJECT FileObject = File;
2625     IO_STATUS_BLOCK Iosb;
2626 
2627     ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2628 
2629     if(Length == 0)
2630     {
2631         KeBugCheck(MEMORY_MANAGEMENT);
2632     }
2633 
2634     FileOffset = *Offset;
2635 
2636     /* Negative/special offset: it cannot be used in this context */
2637     if(FileOffset.u.HighPart < 0)
2638     {
2639         KeBugCheck(MEMORY_MANAGEMENT);
2640     }
2641 
2642     AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2643     OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2644     FileOffset.u.LowPart = AdjustOffset;
2645 
2646     BufferSize = Length + OffsetAdjustment;
2647     BufferSize = PAGE_ROUND_UP(BufferSize);
2648 
2649     /*
2650      * It's ok to use paged pool, because this is a temporary buffer only used in
2651      * the loading of executables. The assumption is that MmCreateSection is
2652      * always called at low IRQLs and that these buffers don't survive a brief
2653      * initialization phase
2654      */
2655     Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, 'rXmM');
2656     if (!Buffer)
2657     {
2658         return STATUS_INSUFFICIENT_RESOURCES;
2659     }
2660 
2661     Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
2662 
2663     UsedSize = (ULONG)Iosb.Information;
2664 
2665     if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2666     {
2667         Status = STATUS_IN_PAGE_ERROR;
2668         ASSERT(!NT_SUCCESS(Status));
2669     }
2670 
2671     if(NT_SUCCESS(Status))
2672     {
2673         *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2674         *AllocBase = Buffer;
2675         *ReadSize = UsedSize - OffsetAdjustment;
2676     }
2677     else
2678     {
2679         ExFreePoolWithTag(Buffer, 'rXmM');
2680     }
2681 
2682     return Status;
2683 }
2684 
2685 #ifdef NASSERT
2686 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2687 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2688 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2689 #else
2690 static
2691 VOID
2692 NTAPI
2693 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2694 {
2695     ULONG i;
2696 
2697     for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2698     {
2699         ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2700                ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2701     }
2702 }
2703 
2704 static
2705 VOID
2706 NTAPI
2707 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2708 {
2709     ULONG i;
2710 
2711     MmspAssertSegmentsSorted(ImageSectionObject);
2712 
2713     for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2714     {
2715         ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2716 
2717         if(i > 0)
2718         {
2719             ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2720                    (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2721                     ImageSectionObject->Segments[i - 1].Length.QuadPart));
2722         }
2723     }
2724 }
2725 
2726 static
2727 VOID
2728 NTAPI
2729 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2730 {
2731     ULONG i;
2732 
2733     for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2734     {
2735         ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2736         ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2737     }
2738 }
2739 #endif
2740 
2741 static
2742 int
2743 __cdecl
2744 MmspCompareSegments(const void * x,
2745                     const void * y)
2746 {
2747     const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2748     const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2749 
2750     if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2751         return 1;
2752     else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2753         return -1;
2754     else
2755         return 0;
2756 }
2757 
2758 /*
2759  * Ensures an image section's segments are sorted in memory
2760  */
2761 static
2762 VOID
2763 NTAPI
2764 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2765                  IN ULONG Flags)
2766 {
2767     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2768     {
2769         MmspAssertSegmentsSorted(ImageSectionObject);
2770     }
2771     else
2772     {
2773         qsort(ImageSectionObject->Segments,
2774               ImageSectionObject->NrSegments,
2775               sizeof(ImageSectionObject->Segments[0]),
2776               MmspCompareSegments);
2777     }
2778 }
2779 
2780 
2781 /*
2782  * Ensures an image section's segments don't overlap in memory and don't have
2783  * gaps and don't have a null size. We let them map to overlapping file regions,
2784  * though - that's not necessarily an error
2785  */
2786 static
2787 BOOLEAN
2788 NTAPI
2789 MmspCheckSegmentBounds
2790 (
2791     IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2792     IN ULONG Flags
2793 )
2794 {
2795     ULONG i;
2796 
2797     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2798     {
2799         MmspAssertSegmentsNoOverlap(ImageSectionObject);
2800         return TRUE;
2801     }
2802 
2803     ASSERT(ImageSectionObject->NrSegments >= 1);
2804 
2805     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2806     {
2807         if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2808         {
2809             return FALSE;
2810         }
2811 
2812         if(i > 0)
2813         {
2814             /*
2815              * TODO: relax the limitation on gaps. For example, gaps smaller than a
2816              * page could be OK (Windows seems to be OK with them), and larger gaps
2817              * could lead to image sections spanning several discontiguous regions
2818              * (NtMapViewOfSection could then refuse to map them, and they could
2819              * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2820              */
2821             if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2822                     ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2823                     ImageSectionObject->Segments[i].Image.VirtualAddress)
2824             {
2825                 return FALSE;
2826             }
2827         }
2828     }
2829 
2830     return TRUE;
2831 }
2832 
2833 /*
2834  * Merges and pads an image section's segments until they all are page-aligned
2835  * and have a size that is a multiple of the page size
2836  */
2837 static
2838 BOOLEAN
2839 NTAPI
2840 MmspPageAlignSegments
2841 (
2842     IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2843     IN ULONG Flags
2844 )
2845 {
2846     ULONG i;
2847     ULONG LastSegment;
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     LastSegment = 0;
2857     EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2858 
2859     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2860     {
2861         /*
2862          * The first segment requires special handling
2863          */
2864         if (i == 0)
2865         {
2866             ULONG_PTR VirtualAddress;
2867             ULONG_PTR VirtualOffset;
2868 
2869             VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2870 
2871             /* Round down the virtual address to the nearest page */
2872             EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2873 
2874             /* Round up the virtual size to the nearest page */
2875             EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2876                                                 EffectiveSegment->Image.VirtualAddress;
2877 
2878             /* Adjust the raw address and size */
2879             VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2880 
2881             if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2882             {
2883                 return FALSE;
2884             }
2885 
2886             /*
2887              * Garbage in, garbage out: unaligned base addresses make the file
2888              * offset point in curious and odd places, but that's what we were
2889              * asked for
2890              */
2891             EffectiveSegment->Image.FileOffset -= VirtualOffset;
2892             EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2893         }
2894         else
2895         {
2896             PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2897             ULONG_PTR EndOfEffectiveSegment;
2898 
2899             EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2900             ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2901 
2902             /*
2903              * The current segment begins exactly where the current effective
2904              * segment ended, therefore beginning a new effective segment
2905              */
2906             if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2907             {
2908                 LastSegment ++;
2909                 ASSERT(LastSegment <= i);
2910                 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2911 
2912                 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2913 
2914                 if (LastSegment != i)
2915                 {
2916                     /*
2917                      * Copy the current segment. If necessary, the effective segment
2918                      * will be expanded later
2919                      */
2920                     *EffectiveSegment = *Segment;
2921                 }
2922 
2923                 /*
2924                  * Page-align the virtual size. We know for sure the virtual address
2925                  * already is
2926                  */
2927                 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2928                 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2929             }
2930             /*
2931              * The current segment is still part of the current effective segment:
2932              * extend the effective segment to reflect this
2933              */
2934             else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2935             {
2936                 static const ULONG FlagsToProtection[16] =
2937                 {
2938                     PAGE_NOACCESS,
2939                     PAGE_READONLY,
2940                     PAGE_READWRITE,
2941                     PAGE_READWRITE,
2942                     PAGE_EXECUTE_READ,
2943                     PAGE_EXECUTE_READ,
2944                     PAGE_EXECUTE_READWRITE,
2945                     PAGE_EXECUTE_READWRITE,
2946                     PAGE_WRITECOPY,
2947                     PAGE_WRITECOPY,
2948                     PAGE_WRITECOPY,
2949                     PAGE_WRITECOPY,
2950                     PAGE_EXECUTE_WRITECOPY,
2951                     PAGE_EXECUTE_WRITECOPY,
2952                     PAGE_EXECUTE_WRITECOPY,
2953                     PAGE_EXECUTE_WRITECOPY
2954                 };
2955 
2956                 unsigned ProtectionFlags;
2957 
2958                 /*
2959                  * Extend the file size
2960                  */
2961 
2962                 /* Unaligned segments must be contiguous within the file */
2963                 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2964                                                   EffectiveSegment->RawLength.QuadPart))
2965                 {
2966                     return FALSE;
2967                 }
2968 
2969                 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2970 
2971                 /*
2972                  * Extend the virtual size
2973                  */
2974                 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2975 
2976                 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2977                                                     EffectiveSegment->Image.VirtualAddress;
2978 
2979                 /*
2980                  * Merge the protection
2981                  */
2982                 EffectiveSegment->Protection |= Segment->Protection;
2983 
2984                 /* Clean up redundance */
2985                 ProtectionFlags = 0;
2986 
2987                 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2988                     ProtectionFlags |= 1 << 0;
2989 
2990                 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2991                     ProtectionFlags |= 1 << 1;
2992 
2993                 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2994                     ProtectionFlags |= 1 << 2;
2995 
2996                 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2997                     ProtectionFlags |= 1 << 3;
2998 
2999                 ASSERT(ProtectionFlags < 16);
3000                 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3001 
3002                 /* If a segment was required to be shared and cannot, fail */
3003                 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3004                         EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3005                 {
3006                     return FALSE;
3007                 }
3008             }
3009             /*
3010              * We assume no holes between segments at this point
3011              */
3012             else
3013             {
3014                 KeBugCheck(MEMORY_MANAGEMENT);
3015             }
3016         }
3017     }
3018     ImageSectionObject->NrSegments = LastSegment + 1;
3019 
3020     return TRUE;
3021 }
3022 
3023 NTSTATUS
3024 ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,
3025                           PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3026 {
3027     LARGE_INTEGER Offset;
3028     PVOID FileHeader;
3029     PVOID FileHeaderBuffer;
3030     ULONG FileHeaderSize;
3031     ULONG Flags;
3032     ULONG OldNrSegments;
3033     NTSTATUS Status;
3034     ULONG i;
3035 
3036     /*
3037      * Read the beginning of the file (2 pages). Should be enough to contain
3038      * all (or most) of the headers
3039      */
3040     Offset.QuadPart = 0;
3041 
3042     Status = ExeFmtpReadFile (FileObject,
3043                               &Offset,
3044                               PAGE_SIZE * 2,
3045                               &FileHeader,
3046                               &FileHeaderBuffer,
3047                               &FileHeaderSize);
3048 
3049     if (!NT_SUCCESS(Status))
3050         return Status;
3051 
3052     if (FileHeaderSize == 0)
3053     {
3054         ExFreePool(FileHeaderBuffer);
3055         return STATUS_UNSUCCESSFUL;
3056     }
3057 
3058     /*
3059      * Look for a loader that can handle this executable
3060      */
3061     for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3062     {
3063         Flags = 0;
3064 
3065         Status = ExeFmtpLoaders[i](FileHeader,
3066                                    FileHeaderSize,
3067                                    FileObject,
3068                                    ImageSectionObject,
3069                                    &Flags,
3070                                    ExeFmtpReadFile,
3071                                    ExeFmtpAllocateSegments);
3072 
3073         if (!NT_SUCCESS(Status))
3074         {
3075             if (ImageSectionObject->Segments)
3076             {
3077                 ExFreePool(ImageSectionObject->Segments);
3078                 ImageSectionObject->Segments = NULL;
3079             }
3080         }
3081 
3082         if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3083             break;
3084     }
3085 
3086     ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3087 
3088     /*
3089      * No loader handled the format
3090      */
3091     if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3092     {
3093         Status = STATUS_INVALID_IMAGE_NOT_MZ;
3094         ASSERT(!NT_SUCCESS(Status));
3095     }
3096 
3097     if (!NT_SUCCESS(Status))
3098         return Status;
3099 
3100     ASSERT(ImageSectionObject->Segments != NULL);
3101     ASSERT(ImageSectionObject->RefCount > 0);
3102 
3103     /*
3104      * Some defaults
3105      */
3106     /* FIXME? are these values platform-dependent? */
3107     if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3108         ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3109 
3110     if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3111         ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3112 
3113     if(ImageSectionObject->BasedAddress == NULL)
3114     {
3115         if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3116             ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3117         else
3118             ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3119     }
3120 
3121     /*
3122      * And now the fun part: fixing the segments
3123      */
3124 
3125     /* Sort them by virtual address */
3126     MmspSortSegments(ImageSectionObject, Flags);
3127 
3128     /* Ensure they don't overlap in memory */
3129     if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3130         return STATUS_INVALID_IMAGE_FORMAT;
3131 
3132     /* Ensure they are aligned */
3133     OldNrSegments = ImageSectionObject->NrSegments;
3134 
3135     if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3136         return STATUS_INVALID_IMAGE_FORMAT;
3137 
3138     /* Trim them if the alignment phase merged some of them */
3139     if (ImageSectionObject->NrSegments < OldNrSegments)
3140     {
3141         PMM_SECTION_SEGMENT Segments;
3142         SIZE_T SizeOfSegments;
3143 
3144         SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3145 
3146         Segments = ExAllocatePoolWithTag(PagedPool,
3147                                          SizeOfSegments,
3148                                          TAG_MM_SECTION_SEGMENT);
3149 
3150         if (Segments == NULL)
3151             return STATUS_INSUFFICIENT_RESOURCES;
3152 
3153         RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3154         ExFreePool(ImageSectionObject->Segments);
3155         ImageSectionObject->Segments = Segments;
3156     }
3157 
3158     /* And finish their initialization */
3159     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3160     {
3161         ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3162         ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3163         ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3164         MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3165         ImageSectionObject->Segments[i].FileObject = FileObject;
3166     }
3167 
3168     ASSERT(ImageSectionObject->RefCount > 0);
3169 
3170     ImageSectionObject->FileObject = FileObject;
3171 
3172     ASSERT(NT_SUCCESS(Status));
3173     return Status;
3174 }
3175 
3176 NTSTATUS
3177 MmCreateImageSection(PSECTION *SectionObject,
3178                      ACCESS_MASK DesiredAccess,
3179                      POBJECT_ATTRIBUTES ObjectAttributes,
3180                      PLARGE_INTEGER UMaximumSize,
3181                      ULONG SectionPageProtection,
3182                      ULONG AllocationAttributes,
3183                      PFILE_OBJECT FileObject)
3184 {
3185     PSECTION Section;
3186     NTSTATUS Status;
3187     PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3188     KIRQL OldIrql;
3189 
3190 
3191     if (FileObject == NULL)
3192         return STATUS_INVALID_FILE_FOR_SECTION;
3193 
3194     if (FileObject->SectionObjectPointer == NULL)
3195     {
3196         DPRINT1("Denying section creation due to missing cache initialization\n");
3197         return STATUS_INVALID_FILE_FOR_SECTION;
3198     }
3199 
3200     /*
3201      * Create the section
3202      */
3203     Status = ObCreateObject (ExGetPreviousMode(),
3204                              MmSectionObjectType,
3205                              ObjectAttributes,
3206                              ExGetPreviousMode(),
3207                              NULL,
3208                              sizeof(*Section),
3209                              0,
3210                              0,
3211                              (PVOID*)(PVOID)&Section);
3212     if (!NT_SUCCESS(Status))
3213     {
3214         return Status;
3215     }
3216 
3217     /*
3218      * Initialize it
3219      */
3220     RtlZeroMemory(Section, sizeof(*Section));
3221 
3222     /* Mark this as a "ROS" Section */
3223     Section->u.Flags.filler = 1;
3224 
3225     Section->InitialPageProtection = SectionPageProtection;
3226     Section->u.Flags.File = 1;
3227     Section->u.Flags.Image = 1;
3228     if (AllocationAttributes & SEC_NO_CHANGE)
3229         Section->u.Flags.NoChange = 1;
3230 
3231 grab_image_section_object:
3232     OldIrql = MiAcquirePfnLock();
3233 
3234     /* Wait for it to be properly created or deleted */
3235     ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3236     while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3237     {
3238         MiReleasePfnLock(OldIrql);
3239 
3240         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
3241 
3242         OldIrql = MiAcquirePfnLock();
3243         ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3244     }
3245 
3246     if (ImageSectionObject == NULL)
3247     {
3248         NTSTATUS StatusExeFmt;
3249 
3250         /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3251         MiReleasePfnLock(OldIrql);
3252 
3253         ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3254         if (ImageSectionObject == NULL)
3255         {
3256             ObDereferenceObject(Section);
3257             return STATUS_NO_MEMORY;
3258         }
3259 
3260         ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3261         ImageSectionObject->RefCount = 1;
3262         ImageSectionObject->SectionCount = 1;
3263 
3264         OldIrql = MiAcquirePfnLock();
3265         if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3266         {
3267             MiReleasePfnLock(OldIrql);
3268             /* Bad luck. Start over */
3269             ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3270             goto grab_image_section_object;
3271         }
3272 
3273         FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3274 
3275         MiReleasePfnLock(OldIrql);
3276 
3277         /* Purge the cache */
3278         CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3279 
3280         StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3281 
3282         if (!NT_SUCCESS(StatusExeFmt))
3283         {
3284             /* Unset */
3285             OldIrql = MiAcquirePfnLock();
3286             FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3287             MiReleasePfnLock(OldIrql);
3288 
3289             if(ImageSectionObject->Segments != NULL)
3290                 ExFreePool(ImageSectionObject->Segments);
3291 
3292             /*
3293              * If image file is empty, then return that the file is invalid for section
3294              */
3295             Status = StatusExeFmt;
3296             if (StatusExeFmt == STATUS_END_OF_FILE)
3297             {
3298                 Status = STATUS_INVALID_FILE_FOR_SECTION;
3299             }
3300 
3301             ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3302             ObDereferenceObject(Section);
3303             return Status;
3304         }
3305 
3306         Section->Segment = (PSEGMENT)ImageSectionObject;
3307         ASSERT(ImageSectionObject->Segments);
3308         ASSERT(ImageSectionObject->RefCount > 0);
3309 
3310         /*
3311          * Lock the file
3312          */
3313         Status = MmspWaitForFileLock(FileObject);
3314         if (!NT_SUCCESS(Status))
3315         {
3316             /* Unset */
3317             OldIrql = MiAcquirePfnLock();
3318             FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3319             MiReleasePfnLock(OldIrql);
3320 
3321             ExFreePool(ImageSectionObject->Segments);
3322             ExFreePool(ImageSectionObject);
3323             ObDereferenceObject(Section);
3324             return Status;
3325         }
3326 
3327         OldIrql = MiAcquirePfnLock();
3328         ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3329 
3330         /* Take a ref on the file on behalf of the newly created structure */
3331         ObReferenceObject(FileObject);
3332 
3333         MiReleasePfnLock(OldIrql);
3334 
3335         Status = StatusExeFmt;
3336     }
3337     else
3338     {
3339         /* If FS driver called for delete, tell them it's not possible anymore. */
3340         ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3341 
3342         /* Take one ref */
3343         InterlockedIncrement64(&ImageSectionObject->RefCount);
3344         ImageSectionObject->SectionCount++;
3345 
3346         MiReleasePfnLock(OldIrql);
3347 
3348         Section->Segment = (PSEGMENT)ImageSectionObject;
3349 
3350         Status = STATUS_SUCCESS;
3351     }
3352     //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3353     *SectionObject = Section;
3354     ASSERT(ImageSectionObject->RefCount > 0);
3355 
3356     return Status;
3357 }
3358 
3359 
3360 
3361 static NTSTATUS
3362 MmMapViewOfSegment(
3363     PMMSUPPORT AddressSpace,
3364     BOOLEAN AsImage,
3365     PMM_SECTION_SEGMENT Segment,
3366     PVOID* BaseAddress,
3367     SIZE_T ViewSize,
3368     ULONG Protect,
3369     LONGLONG ViewOffset,
3370     ULONG AllocationType)
3371 {
3372     PMEMORY_AREA MArea;
3373     NTSTATUS Status;
3374     ULONG Granularity;
3375 
3376     ASSERT(ViewSize != 0);
3377 
3378     if (Segment->WriteCopy)
3379     {
3380         /* We have to do this because the not present fault
3381          * and access fault handlers depend on the protection
3382          * that should be granted AFTER the COW fault takes
3383          * place to be in Region->Protect. The not present fault
3384          * handler changes this to the correct protection for COW when
3385          * mapping the pages into the process's address space. If a COW
3386          * fault takes place, the access fault handler sets the page protection
3387          * to these values for the newly copied pages
3388          */
3389         if (Protect == PAGE_WRITECOPY)
3390             Protect = PAGE_READWRITE;
3391         else if (Protect == PAGE_EXECUTE_WRITECOPY)
3392             Protect = PAGE_EXECUTE_READWRITE;
3393     }
3394 
3395     if (*BaseAddress == NULL)
3396         Granularity = MM_ALLOCATION_GRANULARITY;
3397     else
3398         Granularity = PAGE_SIZE;
3399 
3400 #ifdef NEWCC
3401     if (Segment->Flags & MM_DATAFILE_SEGMENT)
3402     {
3403         LARGE_INTEGER FileOffset;
3404         FileOffset.QuadPart = ViewOffset;
3405         ObReferenceObject(Section);
3406         return _MiMapViewOfSegment(AddressSpace, Segment, BaseAddress, ViewSize, Protect, &FileOffset, AllocationType, __FILE__, __LINE__);
3407     }
3408 #endif
3409     Status = MmCreateMemoryArea(AddressSpace,
3410                                 MEMORY_AREA_SECTION_VIEW,
3411                                 BaseAddress,
3412                                 ViewSize,
3413                                 Protect,
3414                                 &MArea,
3415                                 AllocationType,
3416                                 Granularity);
3417     if (!NT_SUCCESS(Status))
3418     {
3419         DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3420                 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3421         return Status;
3422     }
3423 
3424     InterlockedIncrement64(Segment->ReferenceCount);
3425 
3426     MArea->SectionData.Segment = Segment;
3427     MArea->SectionData.ViewOffset = ViewOffset;
3428     if (AsImage)
3429     {
3430         MArea->VadNode.u.VadFlags.VadType = VadImageMap;
3431     }
3432 
3433     MmInitializeRegion(&MArea->SectionData.RegionListHead,
3434                        ViewSize, 0, Protect);
3435 
3436     return STATUS_SUCCESS;
3437 }
3438 
3439 
3440 static VOID
3441 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3442                   PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3443 {
3444     ULONG_PTR Entry;
3445     LARGE_INTEGER Offset;
3446     SWAPENTRY SavedSwapEntry;
3447     PMM_SECTION_SEGMENT Segment;
3448     PMMSUPPORT AddressSpace;
3449     PEPROCESS Process;
3450 
3451     AddressSpace = (PMMSUPPORT)Context;
3452     Process = MmGetAddressSpaceOwner(AddressSpace);
3453 
3454     Address = (PVOID)PAGE_ROUND_DOWN(Address);
3455 
3456     Offset.QuadPart = ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)) +
3457                       MemoryArea->SectionData.ViewOffset;
3458 
3459     Segment = MemoryArea->SectionData.Segment;
3460 
3461     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3462     while (Entry && MM_IS_WAIT_PTE(Entry))
3463     {
3464         MmUnlockSectionSegment(Segment);
3465         MmUnlockAddressSpace(AddressSpace);
3466 
3467         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
3468 
3469         MmLockAddressSpace(AddressSpace);
3470         MmLockSectionSegment(Segment);
3471         Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3472     }
3473 
3474     /*
3475      * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3476      */
3477     if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3478     {
3479         if (Page == PFN_FROM_SSE(Entry) && Dirty)
3480         {
3481             ASSERT(SwapEntry == 0);
3482         }
3483     }
3484 
3485     if (SwapEntry != 0)
3486     {
3487         /*
3488          * Sanity check
3489          */
3490         MmFreeSwapPage(SwapEntry);
3491     }
3492     else if (Page != 0)
3493     {
3494         if (IS_SWAP_FROM_SSE(Entry) ||
3495                 Page != PFN_FROM_SSE(Entry))
3496         {
3497             ASSERT(Process != NULL);
3498 
3499             /*
3500              * Just dereference private pages
3501              */
3502             SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3503             if (SavedSwapEntry != 0)
3504             {
3505                 MmFreeSwapPage(SavedSwapEntry);
3506                 MmSetSavedSwapEntryPage(Page, 0);
3507             }
3508             MmDeleteRmap(Page, Process, Address);
3509             MmReleasePageMemoryConsumer(MC_USER, Page);
3510         }
3511         else
3512         {
3513             if (Process)
3514             {
3515                 MmDeleteRmap(Page, Process, Address);
3516             }
3517 
3518             /* We don't dirtify for System Space Maps. We let Cc manage that */
3519             MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Process ? Dirty : FALSE, FALSE, NULL);
3520         }
3521     }
3522 }
3523 
3524 static NTSTATUS
3525 MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
3526                      PVOID BaseAddress)
3527 {
3528     NTSTATUS Status;
3529     PMEMORY_AREA MemoryArea;
3530     PMM_SECTION_SEGMENT Segment;
3531     PLIST_ENTRY CurrentEntry;
3532     PMM_REGION CurrentRegion;
3533     PLIST_ENTRY RegionListHead;
3534 
3535     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3536                  BaseAddress);
3537     if (MemoryArea == NULL)
3538     {
3539         return STATUS_UNSUCCESSFUL;
3540     }
3541 
3542     Segment = MemoryArea->SectionData.Segment;
3543 
3544 #ifdef NEWCC
3545     if (Segment->Flags & MM_DATAFILE_SEGMENT)
3546     {
3547         MmUnlockAddressSpace(AddressSpace);
3548         Status = MmUnmapViewOfCacheSegment(AddressSpace, BaseAddress);
3549         MmLockAddressSpace(AddressSpace);
3550 
3551         return Status;
3552     }
3553 #endif
3554 
3555     MemoryArea->DeleteInProgress = TRUE;
3556 
3557     MmLockSectionSegment(Segment);
3558 
3559     RegionListHead = &MemoryArea->SectionData.RegionListHead;
3560     while (!IsListEmpty(RegionListHead))
3561     {
3562         CurrentEntry = RemoveHeadList(RegionListHead);
3563         CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3564         ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3565     }
3566 
3567     if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3568     {
3569         Status = MmFreeMemoryArea(AddressSpace,
3570                                   MemoryArea,
3571                                   NULL,
3572                                   NULL);
3573     }
3574     else
3575     {
3576         Status = MmFreeMemoryArea(AddressSpace,
3577                                   MemoryArea,
3578                                   MmFreeSectionPage,
3579                                   AddressSpace);
3580     }
3581     MmUnlockSectionSegment(Segment);
3582     MmDereferenceSegment(Segment);
3583     return Status;
3584 }
3585 
3586 /* This functions must be called with a locked address space */
3587 NTSTATUS
3588 NTAPI
3589 MiRosUnmapViewOfSection(IN PEPROCESS Process,
3590                         IN PVOID BaseAddress,
3591                         IN BOOLEAN SkipDebuggerNotify)
3592 {
3593     NTSTATUS Status;
3594     PMEMORY_AREA MemoryArea;
3595     PMMSUPPORT AddressSpace;
3596     PVOID ImageBaseAddress = 0;
3597 
3598     DPRINT("Opening memory area Process %p BaseAddress %p\n",
3599            Process, BaseAddress);
3600 
3601     ASSERT(Process);
3602 
3603     AddressSpace = &Process->Vm;
3604 
3605     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3606                  BaseAddress);
3607     if (MemoryArea == NULL ||
3608 #ifdef NEWCC
3609             ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3610 #else
3611             (MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) ||
3612 #endif
3613             MemoryArea->DeleteInProgress)
3614 
3615     {
3616         if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3);
3617 
3618         DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3619         return STATUS_NOT_MAPPED_VIEW;
3620     }
3621 
3622     if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
3623     {
3624         ULONG i;
3625         ULONG NrSegments;
3626         PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3627         PMM_SECTION_SEGMENT SectionSegments;
3628         PMM_SECTION_SEGMENT Segment;
3629 
3630         Segment = MemoryArea->SectionData.Segment;
3631         ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3632         SectionSegments = ImageSectionObject->Segments;
3633         NrSegments = ImageSectionObject->NrSegments;
3634 
3635         MemoryArea->DeleteInProgress = TRUE;
3636 
3637         /* Search for the current segment within the section segments
3638          * and calculate the image base address */
3639         for (i = 0; i < NrSegments; i++)
3640         {
3641             if (Segment == &SectionSegments[i])
3642             {
3643                 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3644                 break;
3645             }
3646         }
3647         if (i >= NrSegments)
3648         {
3649             KeBugCheck(MEMORY_MANAGEMENT);
3650         }
3651 
3652         for (i = 0; i < NrSegments; i++)
3653         {
3654             PVOID SBaseAddress = (PVOID)
3655                                  ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3656 
3657             Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3658             if (!NT_SUCCESS(Status))
3659             {
3660                 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3661                         SBaseAddress, Process, Status);
3662                 ASSERT(NT_SUCCESS(Status));
3663             }
3664         }
3665         DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3666         InterlockedDecrement(&ImageSectionObject->MapCount);
3667     }
3668     else
3669     {
3670         PMM_SECTION_SEGMENT Segment = MemoryArea->SectionData.Segment;
3671         PMMVAD Vad = &MemoryArea->VadNode;
3672         PCONTROL_AREA ControlArea  = Vad->ControlArea;
3673         PFILE_OBJECT FileObject;
3674         SIZE_T ViewSize;
3675         LARGE_INTEGER ViewOffset;
3676         ViewOffset.QuadPart = MemoryArea->SectionData.ViewOffset;
3677 
3678         InterlockedIncrement64(Segment->ReferenceCount);
3679 
3680         ViewSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
3681 
3682         Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3683         if (!NT_SUCCESS(Status))
3684         {
3685             DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3686                     BaseAddress, Process, Status);
3687             ASSERT(NT_SUCCESS(Status));
3688         }
3689 
3690         /* These might be deleted now */
3691         Vad = NULL;
3692         MemoryArea = NULL;
3693 
3694         if (FlagOn(*Segment->Flags, MM_PHYSICALMEMORY_SEGMENT))
3695         {
3696             /* Don't bother */
3697             MmDereferenceSegment(Segment);
3698             return STATUS_SUCCESS;
3699         }
3700         ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT));
3701 
3702         FileObject = Segment->FileObject;
3703         FsRtlAcquireFileExclusive(FileObject);
3704 
3705         /* Don't bother for auto-delete closed file. */
3706         if (FlagOn(FileObject->Flags, FO_DELETE_ON_CLOSE) && FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE))
3707         {
3708             FsRtlReleaseFile(FileObject);
3709             MmDereferenceSegment(Segment);
3710             return STATUS_SUCCESS;
3711         }
3712 
3713         /*
3714          * Flush only when last mapping is deleted.
3715          * FIXME: Why ControlArea == NULL? Or rather: is ControlArea ever not NULL here?
3716          */
3717         if (ControlArea == NULL || ControlArea->NumberOfMappedViews == 1)
3718         {
3719             while (ViewSize > 0)
3720             {
3721                 ULONG FlushSize = min(ViewSize, PAGE_ROUND_DOWN(MAXULONG));
3722                 MmFlushSegment(FileObject->SectionObjectPointer,
3723                                &ViewOffset,
3724                                FlushSize,
3725                                NULL);
3726                 ViewSize -= FlushSize;
3727                 ViewOffset.QuadPart += FlushSize;
3728             }
3729         }
3730 
3731         FsRtlReleaseFile(FileObject);
3732         MmDereferenceSegment(Segment);
3733     }
3734 
3735     /* Notify debugger */
3736     if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3737 
3738     return STATUS_SUCCESS;
3739 }
3740 
3741 
3742 
3743 
3744 /**
3745  * Queries the information of a section object.
3746  *
3747  * @param SectionHandle
3748  *        Handle to the section object. It must be opened with SECTION_QUERY
3749  *        access.
3750  * @param SectionInformationClass
3751  *        Index to a certain information structure. Can be either
3752  *        SectionBasicInformation or SectionImageInformation. The latter
3753  *        is valid only for sections that were created with the SEC_IMAGE
3754  *        flag.
3755  * @param SectionInformation
3756  *        Caller supplies storage for resulting information.
3757  * @param Length
3758  *        Size of the supplied storage.
3759  * @param ResultLength
3760  *        Data written.
3761  *
3762  * @return Status.
3763  *
3764  * @implemented
3765  */
3766 NTSTATUS
3767 NTAPI
3768 NtQuerySection(
3769     _In_ HANDLE SectionHandle,
3770     _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3771     _Out_ PVOID SectionInformation,
3772     _In_ SIZE_T SectionInformationLength,
3773     _Out_opt_ PSIZE_T ResultLength)
3774 {
3775     PSECTION Section;
3776     KPROCESSOR_MODE PreviousMode;
3777     NTSTATUS Status;
3778     PAGED_CODE();
3779 
3780     PreviousMode = ExGetPreviousMode();
3781     if (PreviousMode != KernelMode)
3782     {
3783         _SEH2_TRY
3784         {
3785             ProbeForWrite(SectionInformation,
3786                           SectionInformationLength,
3787                           __alignof(ULONG));
3788             if (ResultLength != NULL)
3789             {
3790                 ProbeForWrite(ResultLength,
3791                               sizeof(*ResultLength),
3792                               __alignof(SIZE_T));
3793             }
3794         }
3795         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3796         {
3797             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3798         }
3799         _SEH2_END;
3800     }
3801 
3802     if (SectionInformationClass == SectionBasicInformation)
3803     {
3804         if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3805         {
3806             return STATUS_INFO_LENGTH_MISMATCH;
3807         }
3808     }
3809     else if (SectionInformationClass == SectionImageInformation)
3810     {
3811         if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3812         {
3813             return STATUS_INFO_LENGTH_MISMATCH;
3814         }
3815     }
3816     else
3817     {
3818         return STATUS_INVALID_INFO_CLASS;
3819     }
3820 
3821     Status = ObReferenceObjectByHandle(SectionHandle,
3822                                        SECTION_QUERY,
3823                                        MmSectionObjectType,
3824                                        PreviousMode,
3825                                        (PVOID*)(PVOID)&Section,
3826                                        NULL);
3827     if (!NT_SUCCESS(Status))
3828     {
3829         DPRINT1("Failed to reference section: 0x%lx\n", Status);
3830         return Status;
3831     }
3832 
3833     switch(SectionInformationClass)
3834     {
3835         case SectionBasicInformation:
3836         {
3837             SECTION_BASIC_INFORMATION Sbi;
3838 
3839             Sbi.Size = Section->SizeOfSection;
3840             Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3841 
3842             Sbi.Attributes = 0;
3843             if (Section->u.Flags.File)
3844                 Sbi.Attributes |= SEC_FILE;
3845             if (Section->u.Flags.Image)
3846                 Sbi.Attributes |= SEC_IMAGE;
3847 
3848             /* Those are not set *************
3849             if (Section->u.Flags.Commit)
3850                 Sbi.Attributes |= SEC_COMMIT;
3851             if (Section->u.Flags.Reserve)
3852                 Sbi.Attributes |= SEC_RESERVE;
3853             **********************************/
3854 
3855             if (Section->u.Flags.Image)
3856             {
3857                 if (MiIsRosSectionObject(Section))
3858                 {
3859                     PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3860                     Sbi.BaseAddress = 0;
3861                     Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3862                 }
3863                 else
3864                 {
3865                     /* Not supported yet */
3866                     ASSERT(FALSE);
3867                 }
3868             }
3869             else if (MiIsRosSectionObject(Section))
3870             {
3871                 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3872                 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3873             }
3874             else
3875             {
3876                 DPRINT1("Unimplemented code path\n");
3877             }
3878 
3879             _SEH2_TRY
3880             {
3881                 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3882                 if (ResultLength != NULL)
3883                 {
3884                     *ResultLength = sizeof(Sbi);
3885                 }
3886             }
3887             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3888             {
3889                 Status = _SEH2_GetExceptionCode();
3890             }
3891             _SEH2_END;
3892             break;
3893         }
3894         case SectionImageInformation:
3895         {
3896             if (!Section->u.Flags.Image)
3897             {
3898                 Status = STATUS_SECTION_NOT_IMAGE;
3899             }
3900             else if (MiIsRosSectionObject(Section))
3901             {
3902                 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3903 
3904                 _SEH2_TRY
3905                 {
3906                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3907                     *Sii = ImageSectionObject->ImageInformation;
3908                     if (ResultLength != NULL)
3909                     {
3910                         *ResultLength = sizeof(*Sii);
3911                     }
3912                 }
3913                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3914                 {
3915                     Status = _SEH2_GetExceptionCode();
3916                 }
3917                 _SEH2_END;
3918             }
3919             else
3920             {
3921                 _SEH2_TRY
3922                 {
3923                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3924                     *Sii = *Section->Segment->u2.ImageInformation;
3925                     if (ResultLength != NULL)
3926                         *ResultLength = sizeof(*Sii);
3927                 }
3928                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3929                 {
3930                     Status = _SEH2_GetExceptionCode();
3931                 }
3932                 _SEH2_END;
3933             }
3934             break;
3935         }
3936         default:
3937             DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3938             Status = STATUS_NOT_SUPPORTED;
3939     }
3940 
3941     ObDereferenceObject(Section);
3942 
3943     return Status;
3944 }
3945 
3946 /**********************************************************************
3947  * NAME       EXPORTED
3948  * MmMapViewOfSection
3949  *
3950  * DESCRIPTION
3951  * Maps a view of a section into the virtual address space of a
3952  * process.
3953  *
3954  * ARGUMENTS
3955  * Section
3956  *  Pointer to the section object.
3957  *
3958  * ProcessHandle
3959  *  Pointer to the process.
3960  *
3961  * BaseAddress
3962  *  Desired base address (or NULL) on entry;
3963  *  Actual base address of the view on exit.
3964  *
3965  * ZeroBits
3966  *  Number of high order address bits that must be zero.
3967  *
3968  * CommitSize
3969  *  Size in bytes of the initially committed section of
3970  *  the view.
3971  *
3972  * SectionOffset
3973  *  Offset in bytes from the beginning of the section
3974  *  to the beginning of the view.
3975  *
3976  * ViewSize
3977  *  Desired length of map (or zero to map all) on entry
3978  *  Actual length mapped on exit.
3979  *
3980  * InheritDisposition
3981  *  Specified how the view is to be shared with
3982  *  child processes.
3983  *
3984  * AllocationType
3985  *  Type of allocation for the pages.
3986  *
3987  * Protect
3988  *  Protection for the committed region of the view.
3989  *
3990  * RETURN VALUE
3991  * Status.
3992  *
3993  * @implemented
3994  */
3995 NTSTATUS NTAPI
3996 MmMapViewOfSection(IN PVOID SectionObject,
3997                    IN PEPROCESS Process,
3998                    IN OUT PVOID *BaseAddress,
3999                    IN ULONG_PTR ZeroBits,
4000                    IN SIZE_T CommitSize,
4001                    IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
4002                    IN OUT PSIZE_T ViewSize,
4003                    IN SECTION_INHERIT InheritDisposition,
4004                    IN ULONG AllocationType,
4005                    IN ULONG Protect)
4006 {
4007     PSECTION Section;
4008     PMMSUPPORT AddressSpace;
4009     NTSTATUS Status = STATUS_SUCCESS;
4010     BOOLEAN NotAtBase = FALSE;
4011     BOOLEAN IsAttached = FALSE;
4012     KAPC_STATE ApcState;
4013 
4014     if (MiIsRosSectionObject(SectionObject) == FALSE)
4015     {
4016         DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
4017         return MmMapViewOfArm3Section(SectionObject,
4018                                       Process,
4019                                       BaseAddress,
4020                                       ZeroBits,
4021                                       CommitSize,
4022                                       SectionOffset,
4023                                       ViewSize,
4024                                       InheritDisposition,
4025                                       AllocationType,
4026                                       Protect);
4027     }
4028 
4029     ASSERT(Process);
4030 
4031     if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION)
4032     {
4033         return STATUS_INVALID_PAGE_PROTECTION;
4034     }
4035 
4036     if (PsGetCurrentProcess() != Process)
4037     {
4038         KeStackAttachProcess(&Process->Pcb, &ApcState);
4039         IsAttached = TRUE;
4040     }
4041 
4042     /* FIXME: We should keep this, but it would break code checking equality */
4043     Protect &= ~PAGE_NOCACHE;
4044 
4045     Section = SectionObject;
4046     AddressSpace = &Process->Vm;
4047 
4048     if (Section->u.Flags.NoChange)
4049         AllocationType |= SEC_NO_CHANGE;
4050 
4051     MmLockAddressSpace(AddressSpace);
4052 
4053     if (Section->u.Flags.Image)
4054     {
4055         ULONG i;
4056         ULONG NrSegments;
4057         ULONG_PTR ImageBase;
4058         SIZE_T ImageSize;
4059         PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4060         PMM_SECTION_SEGMENT SectionSegments;
4061 
4062         ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
4063         SectionSegments = ImageSectionObject->Segments;
4064         NrSegments = ImageSectionObject->NrSegments;
4065 
4066         ASSERT(ImageSectionObject->RefCount > 0);
4067 
4068         ImageBase = (ULONG_PTR)*BaseAddress;
4069         if (ImageBase == 0)
4070         {
4071             ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
4072         }
4073 
4074         ImageSize = 0;
4075         for (i = 0; i < NrSegments; i++)
4076         {
4077             ULONG_PTR MaxExtent;
4078             MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
4079                                     SectionSegments[i].Length.QuadPart);
4080             ImageSize = max(ImageSize, MaxExtent);
4081         }
4082 
4083         ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
4084 
4085         /* Check for an illegal base address */
4086         if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
4087                 ((ImageBase + ImageSize) < ImageSize))
4088         {
4089             ASSERT(*BaseAddress == NULL);
4090             ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
4091                                       MM_VIRTMEM_GRANULARITY);
4092             NotAtBase = TRUE;
4093         }
4094         else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
4095         {
4096             ASSERT(*BaseAddress == NULL);
4097             ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4098             NotAtBase = TRUE;
4099         }
4100 
4101         /* Check there is enough space to map the section at that point. */
4102         if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4103                                        PAGE_ROUND_UP(ImageSize)) != NULL)
4104         {
4105             /* Fail if the user requested a fixed base address. */
4106             if ((*BaseAddress) != NULL)
4107             {
4108                 Status = STATUS_CONFLICTING_ADDRESSES;
4109                 goto Exit;
4110             }
4111             /* Otherwise find a gap to map the image. */
4112             ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE);
4113             if (ImageBase == 0)
4114             {
4115                 Status = STATUS_CONFLICTING_ADDRESSES;
4116                 goto Exit;
4117             }
4118             /* Remember that we loaded image at a different base address */
4119             NotAtBase = TRUE;
4120         }
4121 
4122         for (i = 0; i < NrSegments; i++)
4123         {
4124             PVOID SBaseAddress = (PVOID)
4125                                  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4126             MmLockSectionSegment(&SectionSegments[i]);
4127             Status = MmMapViewOfSegment(AddressSpace,
4128                                         TRUE,
4129                                         &SectionSegments[i],
4130                                         &SBaseAddress,
4131                                         SectionSegments[i].Length.QuadPart,
4132                                         SectionSegments[i].Protection,
4133                                         0,
4134                                         0);
4135             MmUnlockSectionSegment(&SectionSegments[i]);
4136             if (!NT_SUCCESS(Status))
4137             {
4138                 /* roll-back */
4139                 while (i--)
4140                 {
4141                     SBaseAddress =  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4142                     MmLockSectionSegment(&SectionSegments[i]);
4143                     MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4144                     MmUnlockSectionSegment(&SectionSegments[i]);
4145                 }
4146 
4147                 goto Exit;
4148             }
4149         }
4150 
4151         *BaseAddress = (PVOID)ImageBase;
4152         *ViewSize = ImageSize;
4153 
4154         DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4155 
4156         /* One more map */
4157         InterlockedIncrement(&ImageSectionObject->MapCount);
4158     }
4159     else
4160     {
4161         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4162         LONGLONG ViewOffset;
4163 
4164         ASSERT(Segment->RefCount > 0);
4165 
4166         /* check for write access */
4167         if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
4168                 !(Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)))
4169         {
4170             Status = STATUS_SECTION_PROTECTION;
4171             goto Exit;
4172         }
4173         /* check for read access */
4174         if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) &&
4175                 !(Section->InitialPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4176         {
4177             Status = STATUS_SECTION_PROTECTION;
4178             goto Exit;
4179         }
4180         /* check for execute access */
4181         if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) &&
4182                 !(Section->InitialPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4183         {
4184             Status = STATUS_SECTION_PROTECTION;
4185             goto Exit;
4186         }
4187 
4188         if (SectionOffset == NULL)
4189         {
4190             ViewOffset = 0;
4191         }
4192         else
4193         {
4194             ViewOffset = SectionOffset->QuadPart;
4195         }
4196 
4197         if ((ViewOffset % PAGE_SIZE) != 0)
4198         {
4199             Status = STATUS_MAPPED_ALIGNMENT;
4200             goto Exit;
4201         }
4202 
4203         if ((*ViewSize) == 0)
4204         {
4205             (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4206         }
4207         else if ((ExGetPreviousMode() == UserMode) &&
4208             (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4209             (!Section->u.Flags.Reserve))
4210         {
4211             /* Dubious */
4212             (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4213         }
4214 
4215         *ViewSize = PAGE_ROUND_UP(*ViewSize);
4216 
4217         MmLockSectionSegment(Segment);
4218         Status = MmMapViewOfSegment(AddressSpace,
4219                                     FALSE,
4220                                     Segment,
4221                                     BaseAddress,
4222                                     *ViewSize,
4223                                     Protect,
4224                                     ViewOffset,
4225                                     AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
4226         MmUnlockSectionSegment(Segment);
4227         if (!NT_SUCCESS(Status))
4228         {
4229             goto Exit;
4230         }
4231     }
4232 
4233     if (NotAtBase)
4234         Status = STATUS_IMAGE_NOT_AT_BASE;
4235     else
4236         Status = STATUS_SUCCESS;
4237 
4238 Exit:
4239 
4240     MmUnlockAddressSpace(AddressSpace);
4241 
4242     if (IsAttached)
4243     {
4244         KeUnstackDetachProcess(&ApcState);
4245     }
4246 
4247     return Status;
4248 }
4249 
4250 /*
4251  * @unimplemented
4252  */
4253 BOOLEAN
4254 NTAPI
4255 MmCanFileBeTruncated(
4256     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4257     _In_opt_ PLARGE_INTEGER NewFileSize)
4258 {
4259     BOOLEAN Ret;
4260     PMM_SECTION_SEGMENT Segment;
4261 
4262     /* Check whether an ImageSectionObject exists */
4263     if (SectionObjectPointer->ImageSectionObject != NULL)
4264     {
4265         DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4266         return FALSE;
4267     }
4268 
4269     Segment = MiGrabDataSection(SectionObjectPointer);
4270     if (!Segment)
4271     {
4272         /* There is no data section. It's fine to do anything. */
4273         return TRUE;
4274     }
4275 
4276     MmLockSectionSegment(Segment);
4277     if ((Segment->SectionCount == 0) ||
4278         ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4279     {
4280         /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4281         Ret = TRUE;
4282     }
4283     else if (NewFileSize != NULL)
4284     {
4285         /* We can't shrink, but we can extend */
4286         Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4287 #if DBG
4288         if (!Ret)
4289         {
4290             DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4291         }
4292 #endif
4293     }
4294     else
4295     {
4296         DPRINT1("ERROR: File can't be truncated because it has references held to its data section\n");
4297         Ret = FALSE;
4298     }
4299 
4300     MmUnlockSectionSegment(Segment);
4301     MmDereferenceSegment(Segment);
4302 
4303     DPRINT("FIXME: didn't check for outstanding write probes\n");
4304 
4305     return Ret;
4306 }
4307 
4308 static
4309 BOOLEAN
4310 MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment)
4311 {
4312     PCACHE_SECTION_PAGE_TABLE PageTable;
4313 
4314     MmLockSectionSegment(Segment);
4315 
4316     /* Loop over all entries */
4317     for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4318          PageTable != NULL;
4319          PageTable = RtlEnumerateGenericTable(&Segment->PageTable, FALSE))
4320     {
4321         for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4322         {
4323             ULONG_PTR Entry = PageTable->PageEntries[i];
4324             LARGE_INTEGER Offset;
4325 
4326             if (!Entry)
4327                 continue;
4328 
4329             if (IS_SWAP_FROM_SSE(Entry) || (SHARE_COUNT_FROM_SSE(Entry) > 0))
4330             {
4331                 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4332                 MmUnlockSectionSegment(Segment);
4333                 return FALSE;
4334             }
4335 
4336             /* Regular entry */
4337             ASSERT(!IS_WRITE_SSE(Entry));
4338             ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0);
4339 
4340             /* Properly remove using the used API */
4341             Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4342             MmSetPageEntrySectionSegment(Segment, &Offset, 0);
4343             MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4344         }
4345     }
4346 
4347     MmUnlockSectionSegment(Segment);
4348 
4349     return TRUE;
4350 }
4351 
4352 /*
4353  * @implemented
4354  */
4355 BOOLEAN NTAPI
4356 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4357                      IN MMFLUSH_TYPE FlushType)
4358 {
4359     switch(FlushType)
4360     {
4361         case MmFlushForDelete:
4362         {
4363             /*
4364              * FIXME: Check for outstanding write probes on Data section.
4365              * How do we do that ?
4366              */
4367         }
4368         /* Fall-through */
4369         case MmFlushForWrite:
4370         {
4371             KIRQL OldIrql = MiAcquirePfnLock();
4372             PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4373 
4374             DPRINT("Deleting or modifying %p\n", SectionObjectPointer);
4375 
4376             /* Wait for concurrent creation or deletion of image to be done */
4377             ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4378             while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE)))
4379             {
4380                 MiReleasePfnLock(OldIrql);
4381                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4382                 OldIrql = MiAcquirePfnLock();
4383                 ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4384             }
4385 
4386             if (!ImageSectionObject)
4387             {
4388                 DPRINT("No image section object. Accepting\n");
4389                 /* Nothing to do */
4390                 MiReleasePfnLock(OldIrql);
4391                 return TRUE;
4392             }
4393 
4394             /* Do we have open sections or mappings on it ? */
4395             if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount))
4396             {
4397                 /* We do. No way to delete it */
4398                 MiReleasePfnLock(OldIrql);
4399                 DPRINT("Denying. There are mappings open\n");
4400                 return FALSE;
4401             }
4402 
4403             /* There are no sections open on it, but we must still have pages around. Discard everything */
4404             ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
4405             InterlockedIncrement64(&ImageSectionObject->RefCount);
4406             MiReleasePfnLock(OldIrql);
4407 
4408             DPRINT("Purging\n");
4409 
4410             for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
4411             {
4412                 if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i]))
4413                     break;
4414             }
4415 
4416             /* Grab lock again */
4417             OldIrql = MiAcquirePfnLock();
4418 
4419             if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE))
4420             {
4421                 /*
4422                  * Someone actually created a section while we were not looking.
4423                  * Drop our ref and deny.
4424                  * MmDereferenceSegmentWithLock releases Pfn lock
4425                  */
4426                 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4427                 return FALSE;
4428             }
4429 
4430             /* We should be the last one holding a ref here. */
4431             ASSERT(ImageSectionObject->RefCount == 1);
4432             ASSERT(ImageSectionObject->SectionCount == 0);
4433 
4434             /* Dereference the first segment, this will free everything & release the lock */
4435             MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4436             return TRUE;
4437         }
4438     }
4439     return FALSE;
4440 }
4441 
4442 /*
4443  * @implemented
4444  */
4445 NTSTATUS
4446 NTAPI
4447 MmMapViewInSystemSpace (IN PVOID SectionObject,
4448                         OUT PVOID * MappedBase,
4449                         IN OUT PSIZE_T ViewSize)
4450 {
4451     LARGE_INTEGER SectionOffset;
4452 
4453     SectionOffset.QuadPart = 0;
4454 
4455     return MmMapViewInSystemSpaceEx(SectionObject, MappedBase, ViewSize, &SectionOffset, 0);
4456 }
4457 
4458 NTSTATUS
4459 NTAPI
4460 MmMapViewInSystemSpaceEx (
4461     _In_ PVOID SectionObject,
4462     _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase,
4463     _Inout_ PSIZE_T ViewSize,
4464     _Inout_ PLARGE_INTEGER SectionOffset,
4465     _In_ ULONG_PTR Flags
4466     )
4467 {
4468     PSECTION Section = SectionObject;
4469     PMM_SECTION_SEGMENT Segment;
4470     PMMSUPPORT AddressSpace;
4471     NTSTATUS Status;
4472 
4473     UNREFERENCED_PARAMETER(Flags);
4474 
4475     PAGED_CODE();
4476 
4477     if (MiIsRosSectionObject(SectionObject) == FALSE)
4478     {
4479         return MiMapViewInSystemSpace(SectionObject,
4480                                       &MmSession,
4481                                       MappedBase,
4482                                       ViewSize,
4483                                       SectionOffset);
4484     }
4485 
4486     DPRINT("MmMapViewInSystemSpaceEx() called\n");
4487 
4488     /* unsupported for now */
4489     ASSERT(Section->u.Flags.Image == 0);
4490 
4491     Section = SectionObject;
4492     Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4493 
4494     if (*ViewSize == 0)
4495     {
4496         LONGLONG MapSizeLL;
4497 
4498         /* Page-align the mapping */
4499         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4500 
4501         if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4502             return STATUS_INVALID_VIEW_SIZE;
4503 
4504         if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4505             return STATUS_INVALID_VIEW_SIZE;
4506     }
4507     else
4508     {
4509         LONGLONG HelperLL;
4510 
4511         /* Get the map end */
4512         if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4513             return STATUS_INVALID_VIEW_SIZE;
4514 
4515         /* Round it up, if needed */
4516         if (HelperLL % PAGE_SIZE)
4517         {
4518             if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4519                 return STATUS_INVALID_VIEW_SIZE;
4520         }
4521 
4522         /* Now that we have the mapping end, we can align down its start */
4523         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4524 
4525         /* Get the new size */
4526         if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4527             return STATUS_INVALID_VIEW_SIZE;
4528 
4529         if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4530             return STATUS_INVALID_VIEW_SIZE;
4531     }
4532 
4533     AddressSpace = MmGetKernelAddressSpace();
4534 
4535     MmLockAddressSpace(AddressSpace);
4536 
4537     MmLockSectionSegment(Segment);
4538 
4539     Status = MmMapViewOfSegment(AddressSpace,
4540                                 Section->u.Flags.Image,
4541                                 Segment,
4542                                 MappedBase,
4543                                 *ViewSize,
4544                                 PAGE_READWRITE,
4545                                 SectionOffset->QuadPart,
4546                                 SEC_RESERVE);
4547 
4548     MmUnlockSectionSegment(Segment);
4549     MmUnlockAddressSpace(AddressSpace);
4550 
4551     return Status;
4552 }
4553 
4554 /* This function must be called with address space lock held */
4555 NTSTATUS
4556 NTAPI
4557 MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)
4558 {
4559     DPRINT("MmUnmapViewInSystemSpace() called\n");
4560 
4561     return MmUnmapViewOfSegment(MmGetKernelAddressSpace(), MappedBase);
4562 }
4563 
4564 /**********************************************************************
4565  * NAME       EXPORTED
4566  *  MmCreateSection@
4567  *
4568  * DESCRIPTION
4569  *  Creates a section object.
4570  *
4571  * ARGUMENTS
4572  * SectionObject (OUT)
4573  *  Caller supplied storage for the resulting pointer
4574  *  to a SECTION_OBJECT instance;
4575  *
4576  * DesiredAccess
4577  *  Specifies the desired access to the section can be a
4578  *  combination of:
4579  *   STANDARD_RIGHTS_REQUIRED |
4580  *   SECTION_QUERY   |
4581  *   SECTION_MAP_WRITE  |
4582  *   SECTION_MAP_READ  |
4583  *   SECTION_MAP_EXECUTE
4584  *
4585  * ObjectAttributes [OPTIONAL]
4586  *  Initialized attributes for the object can be used
4587  *  to create a named section;
4588  *
4589  * MaximumSize
4590  *  Maximizes the size of the memory section. Must be
4591  *  non-NULL for a page-file backed section.
4592  *  If value specified for a mapped file and the file is
4593  *  not large enough, file will be extended.
4594  *
4595  * SectionPageProtection
4596  *  Can be a combination of:
4597  *   PAGE_READONLY |
4598  *   PAGE_READWRITE |
4599  *   PAGE_WRITEONLY |
4600  *   PAGE_WRITECOPY
4601  *
4602  * AllocationAttributes
4603  *  Can be a combination of:
4604  *   SEC_IMAGE |
4605  *   SEC_RESERVE
4606  *
4607  * FileHandle
4608  *  Handle to a file to create a section mapped to a file
4609  *  instead of a memory backed section;
4610  *
4611  * File
4612  *  Unknown.
4613  *
4614  * RETURN VALUE
4615  *  Status.
4616  *
4617  * @implemented
4618  */
4619 NTSTATUS NTAPI
4620 MmCreateSection (OUT PVOID  * Section,
4621                  IN ACCESS_MASK  DesiredAccess,
4622                  IN POBJECT_ATTRIBUTES ObjectAttributes     OPTIONAL,
4623                  IN PLARGE_INTEGER  MaximumSize,
4624                  IN ULONG   SectionPageProtection,
4625                  IN ULONG   AllocationAttributes,
4626                  IN HANDLE   FileHandle   OPTIONAL,
4627                  IN PFILE_OBJECT  FileObject  OPTIONAL)
4628 {
4629     NTSTATUS Status;
4630     ULONG Protection;
4631     PSECTION *SectionObject = (PSECTION *)Section;
4632     BOOLEAN FileLock = FALSE;
4633 
4634     /* Check if an ARM3 section is being created instead */
4635     if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY)))
4636     {
4637         if (!(FileObject) && !(FileHandle))
4638         {
4639             return MmCreateArm3Section(Section,
4640                                        DesiredAccess,
4641                                        ObjectAttributes,
4642                                        MaximumSize,
4643                                        SectionPageProtection,
4644                                        AllocationAttributes &~ 1,
4645                                        FileHandle,
4646                                        FileObject);
4647         }
4648     }
4649 
4650     /* Convert section flag to page flag */
4651     if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
4652 
4653     /* Check to make sure the protection is correct. Nt* does this already */
4654     Protection = MiMakeProtectionMask(SectionPageProtection);
4655     if (Protection == MM_INVALID_PROTECTION)
4656     {
4657         DPRINT1("Page protection is invalid\n");
4658         return STATUS_INVALID_PAGE_PROTECTION;
4659     }
4660 
4661     /* Check if this is going to be a data or image backed file section */
4662     if ((FileHandle) || (FileObject))
4663     {
4664         /* These cannot be mapped with large pages */
4665         if (AllocationAttributes & SEC_LARGE_PAGES)
4666         {
4667             DPRINT1("Large pages cannot be used with an image mapping\n");
4668             return STATUS_INVALID_PARAMETER_6;
4669         }
4670 
4671         /* Did the caller pass a file object ? */
4672         if (FileObject)
4673         {
4674             /* Reference the object directly */
4675             ObReferenceObject(FileObject);
4676 
4677             /* We don't create image mappings with file objects */
4678             AllocationAttributes &= ~SEC_IMAGE;
4679         }
4680         else
4681         {
4682             /* Reference the file handle to get the object */
4683             Status = ObReferenceObjectByHandle(FileHandle,
4684                                                MmMakeFileAccess[Protection],
4685                                                IoFileObjectType,
4686                                                ExGetPreviousMode(),
4687                                                (PVOID*)&FileObject,
4688                                                NULL);
4689             if (!NT_SUCCESS(Status))
4690             {
4691                 DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4692                 return Status;
4693             }
4694 
4695             /* Lock the file */
4696             Status = FsRtlAcquireToCreateMappedSection(FileObject, SectionPageProtection);
4697             if (!NT_SUCCESS(Status))
4698             {
4699                 ObDereferenceObject(FileObject);
4700                 return Status;
4701             }
4702 
4703             FileLock = TRUE;
4704 
4705             /* Deny access if there are writes on the file */
4706 #if 0
4707             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4708             {
4709                 DPRINT1("Cannot create image maps with writers open on the file!\n");
4710                 Status = STATUS_ACCESS_DENIED;
4711                 goto Quit;
4712             }
4713 #else
4714             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4715                 DPRINT1("Creating image map with writers open on the file!\n");
4716 #endif
4717         }
4718     }
4719     else
4720     {
4721         /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4722         if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
4723     }
4724 
4725     if (AllocationAttributes & SEC_IMAGE)
4726     {
4727         Status = MmCreateImageSection(SectionObject,
4728                                       DesiredAccess,
4729                                       ObjectAttributes,
4730                                       MaximumSize,
4731                                       SectionPageProtection,
4732                                       AllocationAttributes,
4733                                       FileObject);
4734     }
4735 #ifndef NEWCC
4736     else if (FileObject != NULL)
4737     {
4738         Status =  MmCreateDataFileSection(SectionObject,
4739                                           DesiredAccess,
4740                                           ObjectAttributes,
4741                                           MaximumSize,
4742                                           SectionPageProtection,
4743                                           AllocationAttributes,
4744                                           FileObject,
4745                                           FileHandle != NULL);
4746     }
4747 #else
4748     else if (FileHandle != NULL || FileObject != NULL)
4749     {
4750         Status = MmCreateCacheSection(SectionObject,
4751                                       DesiredAccess,
4752                                       ObjectAttributes,
4753                                       MaximumSize,
4754                                       SectionPageProtection,
4755                                       AllocationAttributes,
4756                                       FileObject);
4757     }
4758 #endif
4759     else
4760     {
4761         /* All cases should be handled above */
4762         Status = STATUS_INVALID_PARAMETER;
4763     }
4764 
4765     if (FileLock)
4766         FsRtlReleaseFile(FileObject);
4767     if (FileObject)
4768         ObDereferenceObject(FileObject);
4769 
4770     return Status;
4771 }
4772 
4773 BOOLEAN
4774 NTAPI
4775 MmArePagesResident(
4776     _In_ PEPROCESS Process,
4777     _In_ PVOID Address,
4778     _In_ ULONG Length)
4779 {
4780     PMEMORY_AREA MemoryArea;
4781     BOOLEAN Ret = TRUE;
4782     PMM_SECTION_SEGMENT Segment;
4783     LARGE_INTEGER SegmentOffset, RangeEnd;
4784     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
4785 
4786     MmLockAddressSpace(AddressSpace);
4787 
4788     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
4789     if (MemoryArea == NULL)
4790     {
4791         MmUnlockAddressSpace(AddressSpace);
4792         return FALSE;
4793     }
4794 
4795     /* Only supported in old Mm for now */
4796     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
4797     /* For file mappings */
4798     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
4799 
4800     Segment = MemoryArea->SectionData.Segment;
4801     MmLockSectionSegment(Segment);
4802 
4803     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
4804             + MemoryArea->SectionData.ViewOffset;
4805     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
4806             + MemoryArea->SectionData.ViewOffset;
4807 
4808     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4809     {
4810         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
4811         if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4812         {
4813             Ret = FALSE;
4814             break;
4815         }
4816         SegmentOffset.QuadPart += PAGE_SIZE;
4817     }
4818 
4819     MmUnlockSectionSegment(Segment);
4820 
4821     MmUnlockAddressSpace(AddressSpace);
4822     return Ret;
4823 }
4824 
4825 /* Like CcPurgeCache but for the in-memory segment */
4826 BOOLEAN
4827 NTAPI
4828 MmPurgeSegment(
4829     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4830     _In_opt_ PLARGE_INTEGER Offset,
4831     _In_ ULONG Length)
4832 {
4833     LARGE_INTEGER PurgeStart, PurgeEnd;
4834     PMM_SECTION_SEGMENT Segment;
4835 
4836     Segment = MiGrabDataSection(SectionObjectPointer);
4837     if (!Segment)
4838     {
4839         /* Nothing to purge */
4840         return TRUE;
4841     }
4842 
4843     PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4844     if (Length && Offset)
4845     {
4846         if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4847             return FALSE;
4848     }
4849 
4850     MmLockSectionSegment(Segment);
4851 
4852     if (!Length || !Offset)
4853     {
4854         /* We must calculate the length for ourselves */
4855         /* FIXME: All of this is suboptimal */
4856         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4857         /* No page. Nothing to purge */
4858         if (!ElemCount)
4859         {
4860             MmUnlockSectionSegment(Segment);
4861             MmDereferenceSegment(Segment);
4862             return TRUE;
4863         }
4864 
4865         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4866         PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4867     }
4868 
4869     while (PurgeStart.QuadPart < PurgeEnd.QuadPart)
4870     {
4871         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &PurgeStart);
4872 
4873         if (Entry == 0)
4874         {
4875             PurgeStart.QuadPart += PAGE_SIZE;
4876             continue;
4877         }
4878 
4879         if (IS_SWAP_FROM_SSE(Entry))
4880         {
4881             ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY);
4882             /* The page is currently being read. Meaning someone will need it soon. Bad luck */
4883             MmUnlockSectionSegment(Segment);
4884             MmDereferenceSegment(Segment);
4885             return FALSE;
4886         }
4887 
4888         if (IS_WRITE_SSE(Entry))
4889         {
4890             /* We're trying to purge an entry which is being written. Restart this loop iteration */
4891             MmUnlockSectionSegment(Segment);
4892             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4893             MmLockSectionSegment(Segment);
4894             continue;
4895         }
4896 
4897         if (SHARE_COUNT_FROM_SSE(Entry) > 0)
4898         {
4899             /* This page is currently in use. Bad luck */
4900             MmUnlockSectionSegment(Segment);
4901             MmDereferenceSegment(Segment);
4902             return FALSE;
4903         }
4904 
4905         /* We can let this page go */
4906         MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0);
4907         MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4908 
4909         PurgeStart.QuadPart += PAGE_SIZE;
4910     }
4911 
4912     /* This page is currently in use. Bad luck */
4913     MmUnlockSectionSegment(Segment);
4914     MmDereferenceSegment(Segment);
4915     return TRUE;
4916 }
4917 
4918 NTSTATUS
4919 NTAPI
4920 MmMakeDataSectionResident(
4921     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4922     _In_ LONGLONG Offset,
4923     _In_ ULONG Length,
4924     _In_ PLARGE_INTEGER ValidDataLength)
4925 {
4926     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4927 
4928     /* There must be a segment for this call */
4929     ASSERT(Segment);
4930 
4931     NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength, FALSE);
4932 
4933     MmDereferenceSegment(Segment);
4934 
4935     return Status;
4936 }
4937 
4938 NTSTATUS
4939 NTAPI
4940 MmFlushSegment(
4941     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4942     _In_opt_ PLARGE_INTEGER Offset,
4943     _In_ ULONG Length,
4944     _Out_opt_ PIO_STATUS_BLOCK Iosb)
4945 {
4946     LARGE_INTEGER FlushStart, FlushEnd;
4947     NTSTATUS Status;
4948 
4949     if (Offset)
4950     {
4951         FlushStart = *Offset;
4952         Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart);
4953         if (!NT_SUCCESS(Status))
4954             return Status;
4955     }
4956 
4957     if (Iosb)
4958         Iosb->Information = 0;
4959 
4960     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4961     if (!Segment)
4962     {
4963         /* Nothing to flush */
4964         if (Iosb)
4965             Iosb->Status = STATUS_SUCCESS;
4966         return STATUS_SUCCESS;
4967     }
4968 
4969     ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT);
4970 
4971     MmLockSectionSegment(Segment);
4972 
4973     if (!Offset)
4974     {
4975         FlushStart.QuadPart = 0;
4976 
4977         /* FIXME: All of this is suboptimal */
4978         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4979         /* No page. Nothing to flush */
4980         if (!ElemCount)
4981         {
4982             MmUnlockSectionSegment(Segment);
4983             MmDereferenceSegment(Segment);
4984             if (Iosb)
4985             {
4986                 Iosb->Status = STATUS_SUCCESS;
4987                 Iosb->Information = 0;
4988             }
4989             return STATUS_SUCCESS;
4990         }
4991 
4992         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4993         FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4994     }
4995 
4996     FlushStart.QuadPart >>= PAGE_SHIFT;
4997     FlushStart.QuadPart <<= PAGE_SHIFT;
4998 
4999     while (FlushStart.QuadPart < FlushEnd.QuadPart)
5000     {
5001         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &FlushStart);
5002 
5003         if (IS_DIRTY_SSE(Entry))
5004         {
5005             MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE);
5006 
5007             if (Iosb)
5008                 Iosb->Information += PAGE_SIZE;
5009         }
5010 
5011         FlushStart.QuadPart += PAGE_SIZE;
5012     }
5013 
5014     MmUnlockSectionSegment(Segment);
5015     MmDereferenceSegment(Segment);
5016 
5017     if (Iosb)
5018         Iosb->Status = STATUS_SUCCESS;
5019 
5020     return STATUS_SUCCESS;
5021 }
5022 
5023 _Requires_exclusive_lock_held_(Segment->Lock)
5024 BOOLEAN
5025 NTAPI
5026 MmCheckDirtySegment(
5027     PMM_SECTION_SEGMENT Segment,
5028     PLARGE_INTEGER Offset,
5029     BOOLEAN ForceDirty,
5030     BOOLEAN PageOut)
5031 {
5032     ULONG_PTR Entry;
5033     NTSTATUS Status;
5034     PFN_NUMBER Page;
5035 
5036     ASSERT(Segment->Locked);
5037 
5038     ASSERT((Offset->QuadPart % PAGE_SIZE) == 0);
5039 
5040     DPRINT("Checking segment for file %wZ at offset 0x%I64X.\n", &Segment->FileObject->FileName, Offset->QuadPart);
5041 
5042     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5043     if (Entry == 0)
5044         return FALSE;
5045 
5046     Page = PFN_FROM_SSE(Entry);
5047     if ((IS_DIRTY_SSE(Entry)) || ForceDirty)
5048     {
5049         BOOLEAN DirtyAgain;
5050 
5051         /*
5052          * We got a dirty entry. This path is for the shared data,
5053          * be-it regular file maps or shared sections of DLLs
5054          */
5055         ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) ||
5056                FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5057 
5058         /* Insert the cleaned entry back. Mark it as write in progress, and clear the dirty bit. */
5059         Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
5060         Entry = WRITE_SSE(Entry);
5061         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5062 
5063         MmUnlockSectionSegment(Segment);
5064 
5065         if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5066         {
5067             PERESOURCE ResourceToRelease = NULL;
5068             KIRQL OldIrql;
5069 
5070             /* We have to write it back to the file. Tell the FS driver who we are */
5071             if (PageOut)
5072             {
5073                 LARGE_INTEGER EndOffset = *Offset;
5074 
5075                 ASSERT(IoGetTopLevelIrp() == NULL);
5076 
5077                 /* We need to disable all APCs */
5078                 KeRaiseIrql(APC_LEVEL, &OldIrql);
5079 
5080                 EndOffset.QuadPart += PAGE_SIZE;
5081                 Status = FsRtlAcquireFileForModWriteEx(Segment->FileObject,
5082                                                        &EndOffset,
5083                                                        &ResourceToRelease);
5084                 if (NT_SUCCESS(Status))
5085                 {
5086                     IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
5087                 }
5088                 else
5089                 {
5090                     /* Make sure we will not try to release anything */
5091                     ResourceToRelease = NULL;
5092                 }
5093             }
5094             else
5095             {
5096                 /* We don't have to lock. Say this is success */
5097                 Status = STATUS_SUCCESS;
5098             }
5099 
5100             /* Go ahead and write the page, if previous locking succeeded */
5101             if (NT_SUCCESS(Status))
5102             {
5103                 DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
5104                         Offset->QuadPart, &Segment->FileObject->FileName, PageOut ? "TRUE" : "FALSE");
5105                 Status = MiWritePage(Segment, Offset->QuadPart, Page);
5106             }
5107 
5108             if (PageOut)
5109             {
5110                 IoSetTopLevelIrp(NULL);
5111                 if (ResourceToRelease != NULL)
5112                 {
5113                     FsRtlReleaseFileForModWrite(Segment->FileObject, ResourceToRelease);
5114                 }
5115                 KeLowerIrql(OldIrql);
5116             }
5117         }
5118         else
5119         {
5120             /* This must only be called by the page-out path */
5121             ASSERT(PageOut);
5122 
5123             /* And this must be for a shared section in a DLL */
5124             ASSERT(FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5125 
5126             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5127             if (!SwapEntry)
5128             {
5129                 SwapEntry = MmAllocSwapPage();
5130             }
5131 
5132             if (SwapEntry)
5133             {
5134                 Status = MmWriteToSwapPage(SwapEntry, Page);
5135                 if (NT_SUCCESS(Status))
5136                 {
5137                     MmSetSavedSwapEntryPage(Page, SwapEntry);
5138                 }
5139                 else
5140                 {
5141                     MmFreeSwapPage(SwapEntry);
5142                 }
5143             }
5144             else
5145             {
5146                 DPRINT1("Failed to allocate a swap page!\n");
5147                 Status = STATUS_INSUFFICIENT_RESOURCES;
5148             }
5149         }
5150 
5151         MmLockSectionSegment(Segment);
5152 
5153         /* Get the entry again */
5154         Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5155         ASSERT(PFN_FROM_SSE(Entry) == Page);
5156 
5157         if (!NT_SUCCESS(Status))
5158         {
5159             /* Damn, this failed. Consider this page as still dirty */
5160             DPRINT1("MiWritePage FAILED: Status 0x%08x!\n", Status);
5161             DirtyAgain = TRUE;
5162         }
5163         else
5164         {
5165             /* Check if someone dirtified this page while we were not looking */
5166             DirtyAgain = IS_DIRTY_SSE(Entry);
5167         }
5168 
5169         /* Drop the reference we got, deleting the write altogether. */
5170         Entry = MAKE_SSE(Page << PAGE_SHIFT, SHARE_COUNT_FROM_SSE(Entry) - 1);
5171         if (DirtyAgain)
5172         {
5173             Entry = DIRTY_SSE(Entry);
5174         }
5175         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5176     }
5177 
5178     /* Were this page hanging there just for the sake of being present ? */
5179     if (!IS_DIRTY_SSE(Entry) && (SHARE_COUNT_FROM_SSE(Entry) == 0) && PageOut)
5180     {
5181         ULONG_PTR NewEntry = 0;
5182         /* Restore the swap entry here */
5183         if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5184         {
5185             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5186             if (SwapEntry)
5187                 NewEntry = MAKE_SWAP_SSE(SwapEntry);
5188         }
5189 
5190         /* Yes. Release it */
5191         MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
5192         MmReleasePageMemoryConsumer(MC_USER, Page);
5193         /* Tell the caller we released the page */
5194         return TRUE;
5195     }
5196 
5197     return FALSE;
5198 }
5199 
5200 NTSTATUS
5201 NTAPI
5202 MmMakePagesDirty(
5203     _In_ PEPROCESS Process,
5204     _In_ PVOID Address,
5205     _In_ ULONG Length)
5206 {
5207     PMEMORY_AREA MemoryArea;
5208     PMM_SECTION_SEGMENT Segment;
5209     LARGE_INTEGER SegmentOffset, RangeEnd;
5210     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
5211 
5212     MmLockAddressSpace(AddressSpace);
5213 
5214     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
5215     if (MemoryArea == NULL)
5216     {
5217         DPRINT1("Unable to find memory area at address %p.\n", Address);
5218         MmUnlockAddressSpace(AddressSpace);
5219         return STATUS_NOT_MAPPED_VIEW;
5220     }
5221 
5222     /* Only supported in old Mm for now */
5223     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
5224     /* For file mappings */
5225     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
5226 
5227     Segment = MemoryArea->SectionData.Segment;
5228     MmLockSectionSegment(Segment);
5229 
5230     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
5231             + MemoryArea->SectionData.ViewOffset;
5232     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
5233             + MemoryArea->SectionData.ViewOffset;
5234 
5235     DPRINT("MmMakePagesResident: Segment %p, 0x%I64x -> 0x%I64x\n", Segment, SegmentOffset.QuadPart, RangeEnd.QuadPart);
5236 
5237     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
5238     {
5239         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5240 
5241         /* Let any pending read proceed */
5242         while (MM_IS_WAIT_PTE(Entry))
5243         {
5244             MmUnlockSectionSegment(Segment);
5245             MmUnlockAddressSpace(AddressSpace);
5246             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
5247             MmLockAddressSpace(AddressSpace);
5248             MmLockSectionSegment(Segment);
5249             Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5250         }
5251 
5252         /* We are called from Cc, this can't be backed by the page files */
5253         ASSERT(!IS_SWAP_FROM_SSE(Entry));
5254 
5255         /* If there is no page there, there is nothing to make dirty */
5256         if (Entry != 0)
5257         {
5258             /* Dirtify the entry */
5259             MmSetPageEntrySectionSegment(Segment, &SegmentOffset, DIRTY_SSE(Entry));
5260         }
5261 
5262         SegmentOffset.QuadPart += PAGE_SIZE;
5263     }
5264 
5265     MmUnlockSectionSegment(Segment);
5266 
5267     MmUnlockAddressSpace(AddressSpace);
5268     return STATUS_SUCCESS;
5269 }
5270 
5271 NTSTATUS
5272 NTAPI
5273 MmExtendSection(
5274     _In_ PVOID _Section,
5275     _Inout_ PLARGE_INTEGER NewSize)
5276 {
5277     PSECTION Section = _Section;
5278 
5279     /* It makes no sense to extend an image mapping */
5280     if (Section->u.Flags.Image)
5281         return STATUS_SECTION_NOT_EXTENDED;
5282 
5283     /* Nor is it possible to extend a page file mapping */
5284     if (!Section->u.Flags.File)
5285         return STATUS_SECTION_NOT_EXTENDED;
5286 
5287     if (!MiIsRosSectionObject(Section))
5288         return STATUS_NOT_IMPLEMENTED;
5289 
5290     /* We just extend the sizes. Shrinking is a no-op ? */
5291     if (NewSize->QuadPart > Section->SizeOfSection.QuadPart)
5292     {
5293         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
5294         Section->SizeOfSection = *NewSize;
5295 
5296         if (!Section->u.Flags.Reserve)
5297         {
5298             MmLockSectionSegment(Segment);
5299             if (Segment->RawLength.QuadPart < NewSize->QuadPart)
5300             {
5301                 Segment->RawLength = *NewSize;
5302                 Segment->Length.QuadPart = (NewSize->QuadPart + PAGE_SIZE - 1) & ~((LONGLONG)PAGE_SIZE);
5303             }
5304             MmUnlockSectionSegment(Segment);
5305         }
5306     }
5307 
5308     return STATUS_SUCCESS;
5309 }
5310 
5311 /* EOF */
5312