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