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