xref: /reactos/ntoskrnl/mm/section.c (revision 09dde2cf)
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 ACCESS_MASK MmMakeFileAccess[8];
224 static GENERIC_MAPPING MmpSectionMapping =
225 {
226     STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
227     STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
228     STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
229     SECTION_ALL_ACCESS
230 };
231 
232 
233 /* FUNCTIONS *****************************************************************/
234 
235 
236 
237 NTSTATUS
238 NTAPI
239 MiWritePage(PMM_SECTION_SEGMENT Segment,
240             LONGLONG SegOffset,
241             PFN_NUMBER Page)
242 /*
243  * FUNCTION: write a page for a section backed memory area.
244  * PARAMETERS:
245  *       MemoryArea - Memory area to write the page for.
246  *       Offset - Offset of the page to write.
247  *       Page - Page which contains the data to write.
248  */
249 {
250     NTSTATUS Status;
251     IO_STATUS_BLOCK IoStatus;
252     KEVENT Event;
253     UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)];
254     PMDL Mdl = (PMDL)MdlBase;
255     PFILE_OBJECT FileObject = Segment->FileObject;
256     LARGE_INTEGER FileOffset;
257 
258     FileOffset.QuadPart = Segment->Image.FileOffset + SegOffset;
259 
260     RtlZeroMemory(MdlBase, sizeof(MdlBase));
261     MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
262     MmBuildMdlFromPages(Mdl, &Page);
263     Mdl->MdlFlags |= MDL_PAGES_LOCKED;
264 
265     KeInitializeEvent(&Event, NotificationEvent, FALSE);
266     Status = IoSynchronousPageWrite(FileObject, Mdl, &FileOffset, &Event, &IoStatus);
267     if (Status == STATUS_PENDING)
268     {
269         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
270         Status = IoStatus.Status;
271     }
272     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
273     {
274         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
275     }
276 
277     return Status;
278 }
279 
280 
281 /*
282  References:
283   [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
284       File Format Specification", revision 6.0 (February 1999)
285 */
286 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
287                                   IN SIZE_T FileHeaderSize,
288                                   IN PVOID File,
289                                   OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
290                                   OUT PULONG Flags,
291                                   IN PEXEFMT_CB_READ_FILE ReadFileCb,
292                                   IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
293 {
294     NTSTATUS nStatus;
295     ULONG cbFileHeaderOffsetSize = 0;
296     ULONG cbSectionHeadersOffset = 0;
297     ULONG cbSectionHeadersSize;
298     ULONG cbSectionHeadersOffsetSize = 0;
299     ULONG cbOptHeaderSize;
300     ULONG cbHeadersSize = 0;
301     ULONG nSectionAlignment;
302     ULONG nFileAlignment;
303     ULONG_PTR ImageBase = 0;
304     const IMAGE_DOS_HEADER * pidhDosHeader;
305     const IMAGE_NT_HEADERS32 * pinhNtHeader;
306     const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
307     const IMAGE_SECTION_HEADER * pishSectionHeaders;
308     PMM_SECTION_SEGMENT pssSegments;
309     LARGE_INTEGER lnOffset;
310     PVOID pBuffer;
311     SIZE_T nPrevVirtualEndOfSegment = 0;
312     ULONG nFileSizeOfHeaders = 0;
313     ULONG i;
314     ULONG AlignedLength;
315 
316     ASSERT(FileHeader);
317     ASSERT(FileHeaderSize > 0);
318     ASSERT(File);
319     ASSERT(ImageSectionObject);
320     ASSERT(ReadFileCb);
321     ASSERT(AllocateSegmentsCb);
322 
323     ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
324 
325     ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
326 
327 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
328 
329     pBuffer = NULL;
330     pidhDosHeader = FileHeader;
331 
332     /* DOS HEADER */
333     nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
334 
335     /* image too small to be an MZ executable */
336     if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
337         DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
338 
339     /* no MZ signature */
340     if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
341         DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
342 
343     /* NT HEADER */
344     nStatus = STATUS_INVALID_IMAGE_PROTECT;
345 
346     /* not a Windows executable */
347     if(pidhDosHeader->e_lfanew <= 0)
348         DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
349 
350     if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
351         DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
352 
353     if(FileHeaderSize < cbFileHeaderOffsetSize)
354         pinhNtHeader = NULL;
355     else
356     {
357         /*
358          * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
359          * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
360          */
361         ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
362         pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
363     }
364 
365     /*
366      * the buffer doesn't contain the NT file header, or the alignment is wrong: we
367      * need to read the header from the file
368      */
369     if(FileHeaderSize < cbFileHeaderOffsetSize ||
370             (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
371     {
372         ULONG cbNtHeaderSize;
373         ULONG cbReadSize;
374         PVOID pData;
375 
376 l_ReadHeaderFromFile:
377         cbNtHeaderSize = 0;
378         lnOffset.QuadPart = pidhDosHeader->e_lfanew;
379 
380         /* read the header from the file */
381         nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
382 
383         if(!NT_SUCCESS(nStatus))
384         {
385             NTSTATUS ReturnedStatus = nStatus;
386 
387             /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
388             if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
389 
390             DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
391         }
392 
393         ASSERT(pData);
394         ASSERT(pBuffer);
395         ASSERT(cbReadSize > 0);
396 
397         nStatus = STATUS_INVALID_IMAGE_FORMAT;
398 
399         /* the buffer doesn't contain the file header */
400         if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
401             DIE(("The file doesn't contain the PE file header\n"));
402 
403         pinhNtHeader = pData;
404 
405         /* object still not aligned: copy it to the beginning of the buffer */
406         if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
407         {
408             ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
409             RtlMoveMemory(pBuffer, pData, cbReadSize);
410             pinhNtHeader = pBuffer;
411         }
412 
413         /* invalid NT header */
414         nStatus = STATUS_INVALID_IMAGE_PROTECT;
415 
416         if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
417             DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
418 
419         nStatus = STATUS_INVALID_IMAGE_FORMAT;
420 
421         if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
422             DIE(("The full NT header is too large\n"));
423 
424         /* the buffer doesn't contain the whole NT header */
425         if(cbReadSize < cbNtHeaderSize)
426             DIE(("The file doesn't contain the full NT header\n"));
427     }
428     else
429     {
430         ULONG cbOptHeaderOffsetSize = 0;
431 
432         nStatus = STATUS_INVALID_IMAGE_PROTECT;
433 
434         /* don't trust an invalid NT header */
435         if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
436             DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
437 
438         if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
439             DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
440 
441         nStatus = STATUS_INVALID_IMAGE_FORMAT;
442 
443         if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
444             DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
445 
446         /* the buffer doesn't contain the whole NT header: read it from the file */
447         if(cbOptHeaderOffsetSize > FileHeaderSize)
448             goto l_ReadHeaderFromFile;
449     }
450 
451     /* read information from the NT header */
452     piohOptHeader = &pinhNtHeader->OptionalHeader;
453     cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
454 
455     nStatus = STATUS_INVALID_IMAGE_FORMAT;
456 
457     if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
458         DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
459 
460     /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
461 
462     switch(piohOptHeader->Magic)
463     {
464         case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
465 #ifndef _WIN64
466             nStatus = STATUS_INVALID_IMAGE_WIN_64;
467             DIE(("Win64 optional header, unsupported\n"));
468 #else
469             // Fall through.
470 #endif
471         case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
472             break;
473         default:
474             DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
475     }
476 
477     if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
478             RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
479     {
480         /* See [1], section 3.4.2 */
481         if(piohOptHeader->SectionAlignment < PAGE_SIZE)
482         {
483             if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
484                 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
485         }
486         else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
487             DIE(("The section alignment is smaller than the file alignment\n"));
488 
489         nSectionAlignment = piohOptHeader->SectionAlignment;
490         nFileAlignment = piohOptHeader->FileAlignment;
491 
492         if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
493             DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
494     }
495     else
496     {
497         nSectionAlignment = PAGE_SIZE;
498         nFileAlignment = PAGE_SIZE;
499     }
500 
501     ASSERT(IsPowerOf2(nSectionAlignment));
502     ASSERT(IsPowerOf2(nFileAlignment));
503 
504     switch(piohOptHeader->Magic)
505     {
506     /* PE32 */
507     case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
508     {
509         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
510             ImageBase = piohOptHeader->ImageBase;
511 
512         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
513             ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
514 
515         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
516             ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
517 
518         if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
519             ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
520 
521         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
522         {
523             ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
524 
525             if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
526                     RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
527             {
528                 ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
529                 ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
530             }
531         }
532 
533         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
534         {
535             ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
536                     piohOptHeader->AddressOfEntryPoint);
537         }
538 
539         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
540             ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
541         else
542             ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
543 
544         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
545         {
546             if (piohOptHeader->AddressOfEntryPoint == 0)
547             {
548                 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
549             }
550         }
551 
552         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
553             ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
554 
555         if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
556         {
557             ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
558 
559             /*
560              * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
561              * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
562              * magic to any binary.
563              *
564              * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
565              * but honestly that's not tested. It will also break them when running no ReactOS once we implement
566              * the SxS support -- at which point, duh, this should be removed.
567              *
568              * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
569              */
570             ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
571         }
572 
573         break;
574     }
575 #ifdef _WIN64
576     /* PE64 */
577     case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
578     {
579         const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
580 
581         pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
582 
583         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
584         {
585             ImageBase = pioh64OptHeader->ImageBase;
586             if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
587                 DIE(("ImageBase exceeds the address space\n"));
588         }
589 
590         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
591         {
592             if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
593                 DIE(("SizeOfImage exceeds the address space\n"));
594 
595             ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
596         }
597 
598         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
599         {
600             if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
601                 DIE(("SizeOfStackReserve exceeds the address space\n"));
602 
603             ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
604         }
605 
606         if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
607         {
608             if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
609                 DIE(("SizeOfStackCommit exceeds the address space\n"));
610 
611             ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
612         }
613 
614         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
615         {
616             ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
617 
618             if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
619                     RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
620             {
621                 ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
622                 ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
623             }
624         }
625 
626         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
627         {
628             ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
629                     pioh64OptHeader->AddressOfEntryPoint);
630         }
631 
632         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
633             ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
634         else
635             ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
636 
637         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
638         {
639             if (pioh64OptHeader->AddressOfEntryPoint == 0)
640             {
641                 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
642             }
643         }
644 
645         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
646             ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
647 
648         if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
649             ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
650 
651         break;
652     }
653 #endif // _WIN64
654     }
655 
656     /* [1], section 3.4.2 */
657     if((ULONG_PTR)ImageBase % 0x10000)
658         DIE(("ImageBase is not aligned on a 64KB boundary"));
659 
660     ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
661     ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
662     ImageSectionObject->ImageInformation.GpValue = 0;
663     ImageSectionObject->ImageInformation.ZeroBits = 0;
664     ImageSectionObject->BasedAddress = (PVOID)ImageBase;
665 
666     /* SECTION HEADERS */
667     nStatus = STATUS_INVALID_IMAGE_FORMAT;
668 
669     /* see [1], section 3.3 */
670     if(pinhNtHeader->FileHeader.NumberOfSections > 96)
671         DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
672 
673     /*
674      * the additional segment is for the file's headers. They need to be present for
675      * the benefit of the dynamic loader (to locate exports, defaults for thread
676      * parameters, resources, etc.)
677      */
678     ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
679 
680     /* file offset for the section headers */
681     if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
682         DIE(("Offset overflow\n"));
683 
684     if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
685         DIE(("Offset overflow\n"));
686 
687     /* size of the section headers */
688     ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
689     cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
690 
691     if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
692         DIE(("Section headers too large\n"));
693 
694     /* size of the executable's headers */
695     if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
696     {
697 //        if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
698 //            DIE(("SizeOfHeaders is not aligned\n"));
699 
700         if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
701             DIE(("The section headers overflow SizeOfHeaders\n"));
702 
703         cbHeadersSize = piohOptHeader->SizeOfHeaders;
704     }
705     else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
706         DIE(("Overflow aligning the size of headers\n"));
707 
708     if(pBuffer)
709     {
710         ExFreePool(pBuffer);
711         pBuffer = NULL;
712     }
713     /* WARNING: pinhNtHeader IS NO LONGER USABLE */
714     /* WARNING: piohOptHeader IS NO LONGER USABLE */
715     /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
716 
717     if(FileHeaderSize < cbSectionHeadersOffsetSize)
718         pishSectionHeaders = NULL;
719     else
720     {
721         /*
722          * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
723          * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
724          */
725         ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
726         pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
727     }
728 
729     /*
730      * the buffer doesn't contain the section headers, or the alignment is wrong:
731      * read the headers from the file
732      */
733     if(FileHeaderSize < cbSectionHeadersOffsetSize ||
734             (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
735     {
736         PVOID pData;
737         ULONG cbReadSize;
738 
739         lnOffset.QuadPart = cbSectionHeadersOffset;
740 
741         /* read the header from the file */
742         nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
743 
744         if(!NT_SUCCESS(nStatus))
745             DIE(("ReadFile failed with status %08X\n", nStatus));
746 
747         ASSERT(pData);
748         ASSERT(pBuffer);
749         ASSERT(cbReadSize > 0);
750 
751         nStatus = STATUS_INVALID_IMAGE_FORMAT;
752 
753         /* the buffer doesn't contain all the section headers */
754         if(cbReadSize < cbSectionHeadersSize)
755             DIE(("The file doesn't contain all of the section headers\n"));
756 
757         pishSectionHeaders = pData;
758 
759         /* object still not aligned: copy it to the beginning of the buffer */
760         if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
761         {
762             ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
763             RtlMoveMemory(pBuffer, pData, cbReadSize);
764             pishSectionHeaders = pBuffer;
765         }
766     }
767 
768     /* SEGMENTS */
769     /* allocate the segments */
770     nStatus = STATUS_INSUFFICIENT_RESOURCES;
771     ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
772 
773     if(ImageSectionObject->Segments == NULL)
774         DIE(("AllocateSegments failed\n"));
775 
776     /* initialize the headers segment */
777     pssSegments = ImageSectionObject->Segments;
778 
779 //  ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
780 
781     if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
782         DIE(("Cannot align the size of the section headers\n"));
783 
784     nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
785     if (nPrevVirtualEndOfSegment < cbHeadersSize)
786         DIE(("Cannot align the size of the section headers\n"));
787 
788     pssSegments[0].Image.FileOffset = 0;
789     pssSegments[0].Protection = PAGE_READONLY;
790     pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
791     pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
792     pssSegments[0].Image.VirtualAddress = 0;
793     pssSegments[0].Image.Characteristics = 0;
794     pssSegments[0].WriteCopy = TRUE;
795 
796     /* skip the headers segment */
797     ++ pssSegments;
798 
799     nStatus = STATUS_INVALID_IMAGE_FORMAT;
800 
801     ASSERT(ImageSectionObject->RefCount > 0);
802 
803     /* convert the executable sections into segments. See also [1], section 4 */
804     for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
805     {
806         ULONG nCharacteristics;
807 
808         /* validate the alignment */
809         if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
810             DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
811 
812         /* sections must be contiguous, ordered by base address and non-overlapping */
813         if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
814             DIE(("Memory gap between section %u and the previous\n", i));
815 
816         /* ignore explicit BSS sections */
817         if(pishSectionHeaders[i].PointerToRawData != 0 && pishSectionHeaders[i].SizeOfRawData != 0)
818         {
819             /* validate the alignment */
820 #if 0
821             /* Yes, this should be a multiple of FileAlignment, but there's
822              * stuff out there that isn't. We can cope with that
823              */
824             if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
825                 DIE(("SizeOfRawData[%u] is not aligned\n", i));
826 #endif
827 
828 //            if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
829 //                DIE(("PointerToRawData[%u] is not aligned\n", i));
830 
831             if(!Intsafe_CanAddULong32(pishSectionHeaders[i].PointerToRawData, pishSectionHeaders[i].SizeOfRawData))
832                 DIE(("SizeOfRawData[%u] too large\n", i));
833 
834             /* conversion */
835             pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
836             pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
837         }
838         else
839         {
840             /* FIXME: Should reset PointerToRawData to 0 in the image mapping */
841             ASSERT(pssSegments[i].Image.FileOffset == 0);
842             ASSERT(pssSegments[i].RawLength.QuadPart == 0);
843         }
844 
845         ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
846 
847         nCharacteristics = pishSectionHeaders[i].Characteristics;
848 
849         /* no explicit protection */
850         if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
851         {
852             if(nCharacteristics & IMAGE_SCN_CNT_CODE)
853                 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
854 
855             if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
856                 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
857 
858             if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
859                 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
860         }
861 
862         /* see table above */
863         pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
864         pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
865 
866         if(pishSectionHeaders[i].Misc.VirtualSize == 0)
867             pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
868         else
869             pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
870 
871         AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
872         if(AlignedLength < pssSegments[i].Length.LowPart)
873             DIE(("Cannot align the virtual size of section %u\n", i));
874 
875         pssSegments[i].Length.LowPart = AlignedLength;
876 
877         if(pssSegments[i].Length.QuadPart == 0)
878             DIE(("Virtual size of section %u is null\n", i));
879 
880         pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
881         pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
882 
883         /* ensure the memory image is no larger than 4GB */
884         nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
885         if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
886             DIE(("The image is too large\n"));
887     }
888 
889     if(nSectionAlignment >= PAGE_SIZE)
890         *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
891 
892     /* Success */
893     nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
894 
895 l_Return:
896     if(pBuffer)
897         ExFreePool(pBuffer);
898 
899     return nStatus;
900 }
901 
902 /*
903  * FUNCTION:  Waits in kernel mode indefinitely for a file object lock.
904  * ARGUMENTS: PFILE_OBJECT to wait for.
905  * RETURNS:   Status of the wait.
906  */
907 NTSTATUS
908 MmspWaitForFileLock(PFILE_OBJECT File)
909 {
910     return STATUS_SUCCESS;
911     //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
912 }
913 
914 
915 
916 VOID
917 NTAPI
918 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
919 {
920     ULONG Length;
921     LARGE_INTEGER Offset;
922     ULONG_PTR Entry;
923     SWAPENTRY SavedSwapEntry;
924     PFN_NUMBER Page;
925 
926     Page = 0;
927 
928     MmLockSectionSegment(Segment);
929 
930     Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
931     for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
932     {
933         Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
934         if (Entry)
935         {
936             MmSetPageEntrySectionSegment(Segment, &Offset, 0);
937             if (IS_SWAP_FROM_SSE(Entry))
938             {
939                 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
940             }
941             else
942             {
943                 Page = PFN_FROM_SSE(Entry);
944                 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
945                 if (SavedSwapEntry != 0)
946                 {
947                     MmSetSavedSwapEntryPage(Page, 0);
948                     MmFreeSwapPage(SavedSwapEntry);
949                 }
950                 MmReleasePageMemoryConsumer(MC_USER, Page);
951             }
952         }
953     }
954 
955     MmUnlockSectionSegment(Segment);
956 }
957 
958 static
959 VOID
960 NTAPI
961 FreeSegmentPage(PMM_SECTION_SEGMENT Segment, PLARGE_INTEGER Offset)
962 {
963     ULONG_PTR Entry;
964     PFN_NUMBER Page;
965 
966     MmLockSectionSegment(Segment);
967 
968     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
969 
970     MmUnlockSectionSegment(Segment);
971 
972     /* This must be either a valid entry or nothing */
973     ASSERT(!IS_SWAP_FROM_SSE(Entry));
974 
975     /* There should be no reference anymore */
976     ASSERT(SHARE_COUNT_FROM_SSE(Entry) == 0);
977 
978     Page = PFN_FROM_SSE(Entry);
979     /* If there is a page, this must be because it's still dirty */
980     ASSERT(Page != 0);
981 
982     /* Write the page */
983     if (IS_DIRTY_SSE(Entry))
984         MiWritePage(Segment, Offset->QuadPart, Page);
985 
986     MmReleasePageMemoryConsumer(MC_USER, Page);
987 }
988 
989 _When_(OldIrql == MM_NOIRQL, _IRQL_requires_max_(DISPATCH_LEVEL))
990 _When_(OldIrql == MM_NOIRQL, _Requires_lock_not_held_(MmPfnLock))
991 _When_(OldIrql != MM_NOIRQL, _Requires_lock_held_(MmPfnLock))
992 _When_(OldIrql != MM_NOIRQL, _Releases_lock_(MmPfnLock))
993 _When_(OldIrql != MM_NOIRQL, _IRQL_requires_(DISPATCH_LEVEL))
994 VOID
995 NTAPI
996 MmDereferenceSegmentWithLock(
997     _Inout_ PMM_SECTION_SEGMENT Segment,
998     _In_ _When_(OldIrql != MM_NOIRQL, _IRQL_restores_) 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     PFN_NUMBER UnmappedPage;
1900     PVOID PAddress;
1901     LARGE_INTEGER Offset;
1902     PMM_REGION Region;
1903     ULONG_PTR Entry;
1904     PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1905     BOOLEAN Cow = FALSE;
1906     ULONG NewProtect;
1907     BOOLEAN Unmapped;
1908 
1909     DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1910 
1911     /* Get the region for this address */
1912     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1913                         &MemoryArea->SectionData.RegionListHead,
1914                         Address, NULL);
1915     ASSERT(Region != NULL);
1916     if (!(Region->Protect & PAGE_IS_WRITABLE))
1917         return STATUS_ACCESS_VIOLATION;
1918 
1919     /* Make sure we have a page mapping for this address.  */
1920     if (!MmIsPagePresent(Process, Address))
1921     {
1922         NTSTATUS Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, Locked);
1923         if (!NT_SUCCESS(Status))
1924         {
1925             /* This is invalid access ! */
1926             return Status;
1927         }
1928     }
1929 
1930     /*
1931      * Check if the page has already been set readwrite
1932      */
1933     if (MmGetPageProtect(Process, Address) & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE))
1934     {
1935         DPRINT("Address 0x%p\n", Address);
1936         return STATUS_SUCCESS;
1937     }
1938 
1939     /* Check if we are doing Copy-On-Write */
1940     Segment = MemoryArea->SectionData.Segment;
1941     Cow = Segment->WriteCopy || (Region->Protect & PAGE_IS_WRITECOPY);
1942 
1943     if (!Cow)
1944     {
1945         /* Simply update page protection and we're done */
1946         MmSetPageProtect(Process, Address, Region->Protect);
1947         return STATUS_SUCCESS;
1948     }
1949 
1950     /* Calculate the new protection & check if we should update the region */
1951     NewProtect = Region->Protect;
1952     if (NewProtect & PAGE_IS_WRITECOPY)
1953     {
1954         NewProtect &= ~PAGE_IS_WRITECOPY;
1955         if (Region->Protect & PAGE_IS_EXECUTABLE)
1956             NewProtect |= PAGE_EXECUTE_READWRITE;
1957         else
1958             NewProtect |= PAGE_READWRITE;
1959         MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1960                 &MemoryArea->SectionData.RegionListHead,
1961                 Address, PAGE_SIZE, Region->Type, NewProtect,
1962                 MmAlterViewAttributes);
1963     }
1964 
1965     /*
1966      * Find the offset of the page
1967      */
1968     PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1969     Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1970                       + MemoryArea->SectionData.ViewOffset;
1971 
1972     /* Get the page mapping this section offset. */
1973     MmLockSectionSegment(Segment);
1974     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1975 
1976     /* Get the current page mapping for the process */
1977     ASSERT(MmIsPagePresent(Process, PAddress));
1978     OldPage = MmGetPfnForProcess(Process, PAddress);
1979     ASSERT(OldPage != 0);
1980 
1981     if (IS_SWAP_FROM_SSE(Entry) ||
1982             PFN_FROM_SSE(Entry) != OldPage)
1983     {
1984         MmUnlockSectionSegment(Segment);
1985         /* This is a private page. We must only change the page protection. */
1986         MmSetPageProtect(Process, PAddress, NewProtect);
1987         return STATUS_SUCCESS;
1988     }
1989 
1990     /*
1991      * Allocate a page
1992      */
1993     if (!NT_SUCCESS(MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage)))
1994     {
1995         KeBugCheck(MEMORY_MANAGEMENT);
1996     }
1997 
1998     /*
1999      * Copy the old page
2000      */
2001     NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
2002 
2003     /*
2004      * Unshare the old page.
2005      */
2006     DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
2007     Unmapped = MmDeleteVirtualMapping(Process, PAddress, NULL, &UnmappedPage);
2008     if (!Unmapped || (UnmappedPage != OldPage))
2009     {
2010         /* Uh , we had a page just before, but suddenly it changes. Someone corrupted us. */
2011         KeBugCheckEx(MEMORY_MANAGEMENT,
2012                     (ULONG_PTR)Process,
2013                     (ULONG_PTR)PAddress,
2014                     (ULONG_PTR)__FILE__,
2015                     __LINE__);
2016     }
2017 
2018     if (Process)
2019         MmDeleteRmap(OldPage, Process, PAddress);
2020     MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, FALSE, FALSE, NULL);
2021     MmUnlockSectionSegment(Segment);
2022 
2023     /*
2024      * Set the PTE to point to the new page
2025      */
2026     if (!NT_SUCCESS(MmCreateVirtualMapping(Process, PAddress, NewProtect, NewPage)))
2027     {
2028         DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
2029         KeBugCheck(MEMORY_MANAGEMENT);
2030     }
2031 
2032     if (Process)
2033         MmInsertRmap(NewPage, Process, PAddress);
2034 
2035     DPRINT("Address 0x%p\n", Address);
2036     return STATUS_SUCCESS;
2037 }
2038 
2039 NTSTATUS
2040 NTAPI
2041 MmProtectSectionView(PMMSUPPORT AddressSpace,
2042                      PMEMORY_AREA MemoryArea,
2043                      PVOID BaseAddress,
2044                      SIZE_T Length,
2045                      ULONG Protect,
2046                      PULONG OldProtect)
2047 {
2048     PMM_REGION Region;
2049     NTSTATUS Status;
2050     ULONG_PTR MaxLength;
2051 
2052     MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2053     if (Length > MaxLength)
2054         Length = (ULONG)MaxLength;
2055 
2056     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2057                           &MemoryArea->SectionData.RegionListHead,
2058                           BaseAddress, NULL);
2059     ASSERT(Region != NULL);
2060 
2061     if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2062             Region->Protect != Protect)
2063     {
2064         return STATUS_INVALID_PAGE_PROTECTION;
2065     }
2066 
2067     *OldProtect = Region->Protect;
2068     Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2069                            &MemoryArea->SectionData.RegionListHead,
2070                            BaseAddress, Length, Region->Type, Protect,
2071                            MmAlterViewAttributes);
2072 
2073     return Status;
2074 }
2075 
2076 NTSTATUS NTAPI
2077 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2078                    PVOID Address,
2079                    PMEMORY_BASIC_INFORMATION Info,
2080                    PSIZE_T ResultLength)
2081 {
2082     PMM_REGION Region;
2083     PVOID RegionBaseAddress;
2084     PMM_SECTION_SEGMENT Segment;
2085 
2086     Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2087                           &MemoryArea->SectionData.RegionListHead,
2088                           Address, &RegionBaseAddress);
2089     if (Region == NULL)
2090     {
2091         return STATUS_UNSUCCESSFUL;
2092     }
2093 
2094     if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
2095     {
2096         Segment = MemoryArea->SectionData.Segment;
2097         Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2098         Info->Type = MEM_IMAGE;
2099     }
2100     else
2101     {
2102         Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2103         Info->Type = MEM_MAPPED;
2104     }
2105     Info->BaseAddress = RegionBaseAddress;
2106     Info->AllocationProtect = MmProtectToValue[MemoryArea->VadNode.u.VadFlags.Protection];
2107     Info->RegionSize = Region->Length;
2108     Info->State = MEM_COMMIT;
2109     Info->Protect = Region->Protect;
2110 
2111     *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2112     return STATUS_SUCCESS;
2113 }
2114 
2115 VOID NTAPI
2116 MmpDeleteSection(PVOID ObjectBody)
2117 {
2118     PSECTION Section = ObjectBody;
2119 
2120     /* Check if it's an ARM3, or ReactOS section */
2121     if (!MiIsRosSectionObject(Section))
2122     {
2123         MiDeleteARM3Section(ObjectBody);
2124         return;
2125     }
2126 
2127     DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2128     if (Section->u.Flags.Image)
2129     {
2130         PMM_IMAGE_SECTION_OBJECT ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)Section->Segment;
2131 
2132         /*
2133          * NOTE: Section->ImageSection can be NULL for short time
2134          * during the section creating. If we fail for some reason
2135          * until the image section is properly initialized we shouldn't
2136          * process further here.
2137          */
2138         if (Section->Segment == NULL)
2139             return;
2140 
2141         KIRQL OldIrql = MiAcquirePfnLock();
2142         ImageSectionObject->SectionCount--;
2143 
2144         /* We just dereference the first segment */
2145         ASSERT(ImageSectionObject->RefCount > 0);
2146         /* MmDereferenceSegmentWithLock releases PFN lock */
2147         MmDereferenceSegmentWithLock(ImageSectionObject->Segments, OldIrql);
2148     }
2149     else
2150     {
2151         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
2152 
2153         /*
2154          * NOTE: Section->Segment can be NULL for short time
2155          * during the section creating.
2156          */
2157         if (Segment == NULL)
2158             return;
2159 
2160         KIRQL OldIrql = MiAcquirePfnLock();
2161         Segment->SectionCount--;
2162 
2163         /* MmDereferenceSegmentWithLock releases PFN lock */
2164         MmDereferenceSegmentWithLock(Segment, OldIrql);
2165     }
2166 }
2167 
2168 VOID NTAPI
2169 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2170                 IN PVOID Object,
2171                 IN ACCESS_MASK GrantedAccess,
2172                 IN ULONG ProcessHandleCount,
2173                 IN ULONG SystemHandleCount)
2174 {
2175     DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2176 }
2177 
2178 CODE_SEG("INIT")
2179 NTSTATUS
2180 NTAPI
2181 MmCreatePhysicalMemorySection(VOID)
2182 {
2183     PSECTION PhysSection;
2184     NTSTATUS Status;
2185     OBJECT_ATTRIBUTES Obj;
2186     UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2187     LARGE_INTEGER SectionSize;
2188     HANDLE Handle;
2189     PMM_SECTION_SEGMENT Segment;
2190 
2191     /*
2192      * Create the section mapping physical memory
2193      */
2194     SectionSize.QuadPart = MmHighestPhysicalPage * PAGE_SIZE;
2195     InitializeObjectAttributes(&Obj,
2196                                &Name,
2197                                OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
2198                                NULL,
2199                                NULL);
2200     /*
2201      * Create the Object
2202      */
2203     Status = ObCreateObject(KernelMode,
2204                             MmSectionObjectType,
2205                             &Obj,
2206                             ExGetPreviousMode(),
2207                             NULL,
2208                             sizeof(*PhysSection),
2209                             0,
2210                             0,
2211                             (PVOID*)&PhysSection);
2212     if (!NT_SUCCESS(Status))
2213     {
2214         DPRINT1("MmCreatePhysicalMemorySection: failed to create object (0x%lx)\n", Status);
2215         return Status;
2216     }
2217 
2218     /*
2219      * Initialize it
2220      */
2221     RtlZeroMemory(PhysSection, sizeof(*PhysSection));
2222 
2223     /* Mark this as a "ROS Section" */
2224     PhysSection->u.Flags.filler = 1;
2225     PhysSection->InitialPageProtection = PAGE_EXECUTE_READWRITE;
2226     PhysSection->u.Flags.PhysicalMemory = 1;
2227     PhysSection->SizeOfSection = SectionSize;
2228     Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2229                                     TAG_MM_SECTION_SEGMENT);
2230     if (Segment == NULL)
2231     {
2232         ObDereferenceObject(PhysSection);
2233         return STATUS_NO_MEMORY;
2234     }
2235     RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2236     PhysSection->Segment = (PSEGMENT)Segment;
2237     Segment->RefCount = 1;
2238 
2239     Segment->ReferenceCount = &Segment->RefCount;
2240     Segment->Flags = &Segment->SegFlags;
2241 
2242     ExInitializeFastMutex(&Segment->Lock);
2243     Segment->Image.FileOffset = 0;
2244     Segment->Protection = PAGE_EXECUTE_READWRITE;
2245     Segment->RawLength = SectionSize;
2246     Segment->Length = SectionSize;
2247     Segment->SegFlags = MM_PHYSICALMEMORY_SEGMENT;
2248     Segment->WriteCopy = FALSE;
2249     Segment->Image.VirtualAddress = 0;
2250     Segment->Image.Characteristics = 0;
2251     MiInitializeSectionPageTable(Segment);
2252 
2253     Status = ObInsertObject(PhysSection,
2254                             NULL,
2255                             SECTION_ALL_ACCESS,
2256                             0,
2257                             NULL,
2258                             &Handle);
2259     if (!NT_SUCCESS(Status))
2260     {
2261         ObDereferenceObject(PhysSection);
2262         return Status;
2263     }
2264     ObCloseHandle(Handle, KernelMode);
2265 
2266     return STATUS_SUCCESS;
2267 }
2268 
2269 CODE_SEG("INIT")
2270 NTSTATUS
2271 NTAPI
2272 MmInitSectionImplementation(VOID)
2273 {
2274     OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2275     UNICODE_STRING Name;
2276 
2277     DPRINT("Creating Section Object Type\n");
2278 
2279     /* Initialize the section based root */
2280     ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2281     MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2282 
2283     /* Initialize the Section object type  */
2284     RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2285     RtlInitUnicodeString(&Name, L"Section");
2286     ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2287     ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(SECTION);
2288     ObjectTypeInitializer.PoolType = PagedPool;
2289     ObjectTypeInitializer.UseDefaultObject = TRUE;
2290     ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2291     ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2292     ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2293     ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2294     ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2295     ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2296 
2297     MmCreatePhysicalMemorySection();
2298 
2299     return STATUS_SUCCESS;
2300 }
2301 
2302 static
2303 NTSTATUS
2304 NTAPI
2305 MmCreateDataFileSection(PSECTION *SectionObject,
2306                         ACCESS_MASK DesiredAccess,
2307                         POBJECT_ATTRIBUTES ObjectAttributes,
2308                         PLARGE_INTEGER UMaximumSize,
2309                         ULONG SectionPageProtection,
2310                         ULONG AllocationAttributes,
2311                         PFILE_OBJECT FileObject,
2312                         BOOLEAN GotFileHandle)
2313 /*
2314  * Create a section backed by a data file
2315  */
2316 {
2317     PSECTION Section;
2318     NTSTATUS Status;
2319     LARGE_INTEGER MaximumSize;
2320     PMM_SECTION_SEGMENT Segment;
2321     KIRQL OldIrql;
2322 
2323     /*
2324      * Create the section
2325      */
2326     Status = ObCreateObject(ExGetPreviousMode(),
2327                             MmSectionObjectType,
2328                             ObjectAttributes,
2329                             ExGetPreviousMode(),
2330                             NULL,
2331                             sizeof(*Section),
2332                             0,
2333                             0,
2334                             (PVOID*)&Section);
2335     if (!NT_SUCCESS(Status))
2336     {
2337         return Status;
2338     }
2339     /*
2340      * Initialize it
2341      */
2342     RtlZeroMemory(Section, sizeof(*Section));
2343 
2344     /* Mark this as a "ROS" section */
2345     Section->u.Flags.filler = 1;
2346     Section->InitialPageProtection = SectionPageProtection;
2347     Section->u.Flags.File = 1;
2348 
2349     if (AllocationAttributes & SEC_NO_CHANGE)
2350         Section->u.Flags.NoChange = 1;
2351     if (AllocationAttributes & SEC_RESERVE)
2352         Section->u.Flags.Reserve = 1;
2353 
2354     if (!GotFileHandle)
2355     {
2356         ASSERT(UMaximumSize != NULL);
2357         // ASSERT(UMaximumSize->QuadPart != 0);
2358         MaximumSize = *UMaximumSize;
2359     }
2360     else
2361     {
2362         LARGE_INTEGER FileSize;
2363         Status = FsRtlGetFileSize(FileObject, &FileSize);
2364         if (!NT_SUCCESS(Status))
2365         {
2366             ObDereferenceObject(Section);
2367             return Status;
2368         }
2369 
2370         /*
2371          * FIXME: Revise this once a locking order for file size changes is
2372          * decided
2373          */
2374         if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2375         {
2376             MaximumSize = *UMaximumSize;
2377         }
2378         else
2379         {
2380             MaximumSize = FileSize;
2381             /* Mapping zero-sized files isn't allowed. */
2382             if (MaximumSize.QuadPart == 0)
2383             {
2384                 ObDereferenceObject(Section);
2385                 return STATUS_MAPPED_FILE_SIZE_ZERO;
2386             }
2387         }
2388 
2389         if (MaximumSize.QuadPart > FileSize.QuadPart)
2390         {
2391             Status = IoSetInformation(FileObject,
2392                                       FileEndOfFileInformation,
2393                                       sizeof(LARGE_INTEGER),
2394                                       &MaximumSize);
2395             if (!NT_SUCCESS(Status))
2396             {
2397                 ObDereferenceObject(Section);
2398                 return STATUS_SECTION_NOT_EXTENDED;
2399             }
2400         }
2401     }
2402 
2403     if (FileObject->SectionObjectPointer == NULL)
2404     {
2405         ObDereferenceObject(Section);
2406         return STATUS_INVALID_FILE_FOR_SECTION;
2407     }
2408 
2409     /*
2410      * Lock the file
2411      */
2412     Status = MmspWaitForFileLock(FileObject);
2413     if (Status != STATUS_SUCCESS)
2414     {
2415         ObDereferenceObject(Section);
2416         return Status;
2417     }
2418 
2419     /* Lock the PFN lock while messing with Section Object pointers */
2420 grab_segment:
2421     OldIrql = MiAcquirePfnLock();
2422     Segment = FileObject->SectionObjectPointer->DataSectionObject;
2423 
2424     while (Segment && (Segment->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
2425     {
2426         MiReleasePfnLock(OldIrql);
2427         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
2428         OldIrql = MiAcquirePfnLock();
2429         Segment = FileObject->SectionObjectPointer->DataSectionObject;
2430     }
2431 
2432     /*
2433      * If this file hasn't been mapped as a data file before then allocate a
2434      * section segment to describe the data file mapping
2435      */
2436     if (Segment == NULL)
2437     {
2438         /* Release the lock. ExAllocatePoolWithTag might acquire it */
2439         MiReleasePfnLock(OldIrql);
2440 
2441         Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2442                                         TAG_MM_SECTION_SEGMENT);
2443         if (Segment == NULL)
2444         {
2445             //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2446             ObDereferenceObject(Section);
2447             return STATUS_NO_MEMORY;
2448         }
2449 
2450         /* We are creating it */
2451         RtlZeroMemory(Segment, sizeof(*Segment));
2452         Segment->SegFlags = MM_DATAFILE_SEGMENT | MM_SEGMENT_INCREATE;
2453         Segment->RefCount = 1;
2454 
2455         /* Acquire lock again */
2456         OldIrql = MiAcquirePfnLock();
2457 
2458         if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
2459         {
2460             /* Well that's bad luck. Restart it all over */
2461             MiReleasePfnLock(OldIrql);
2462             ExFreePoolWithTag(Segment, TAG_MM_SECTION_SEGMENT);
2463             goto grab_segment;
2464         }
2465 
2466         FileObject->SectionObjectPointer->DataSectionObject = Segment;
2467 
2468         /* We're safe to release the lock now */
2469         MiReleasePfnLock(OldIrql);
2470 
2471         Section->Segment = (PSEGMENT)Segment;
2472 
2473         /* Self-referencing segment */
2474         Segment->Flags = &Segment->SegFlags;
2475         Segment->ReferenceCount = &Segment->RefCount;
2476 
2477         Segment->SectionCount = 1;
2478 
2479         ExInitializeFastMutex(&Segment->Lock);
2480         Segment->FileObject = FileObject;
2481         ObReferenceObject(FileObject);
2482 
2483         Segment->Image.FileOffset = 0;
2484         Segment->Protection = SectionPageProtection;
2485 
2486         Segment->Image.Characteristics = 0;
2487         Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
2488         if (AllocationAttributes & SEC_RESERVE)
2489         {
2490             Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
2491         }
2492         else
2493         {
2494             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2495             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2496         }
2497         Segment->Image.VirtualAddress = 0;
2498         MiInitializeSectionPageTable(Segment);
2499 
2500         /* We're good to use it now */
2501         OldIrql = MiAcquirePfnLock();
2502         Segment->SegFlags &= ~MM_SEGMENT_INCREATE;
2503         MiReleasePfnLock(OldIrql);
2504     }
2505     else
2506     {
2507         Section->Segment = (PSEGMENT)Segment;
2508         InterlockedIncrement64(&Segment->RefCount);
2509         InterlockedIncrementUL(&Segment->SectionCount);
2510 
2511         MiReleasePfnLock(OldIrql);
2512 
2513         MmLockSectionSegment(Segment);
2514 
2515         if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
2516                 !(AllocationAttributes & SEC_RESERVE))
2517         {
2518             Segment->RawLength.QuadPart = MaximumSize.QuadPart;
2519             Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
2520         }
2521 
2522         MmUnlockSectionSegment(Segment);
2523     }
2524     Section->SizeOfSection = MaximumSize;
2525 
2526     //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
2527     *SectionObject = Section;
2528     return STATUS_SUCCESS;
2529 }
2530 
2531 /*
2532  TODO: not that great (declaring loaders statically, having to declare all of
2533  them, having to keep them extern, etc.), will fix in the future
2534 */
2535 extern NTSTATUS NTAPI PeFmtCreateSection
2536 (
2537     IN CONST VOID * FileHeader,
2538     IN SIZE_T FileHeaderSize,
2539     IN PVOID File,
2540     OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2541     OUT PULONG Flags,
2542     IN PEXEFMT_CB_READ_FILE ReadFileCb,
2543     IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2544 );
2545 
2546 extern NTSTATUS NTAPI ElfFmtCreateSection
2547 (
2548     IN CONST VOID * FileHeader,
2549     IN SIZE_T FileHeaderSize,
2550     IN PVOID File,
2551     OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2552     OUT PULONG Flags,
2553     IN PEXEFMT_CB_READ_FILE ReadFileCb,
2554     IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
2555 );
2556 
2557 static PEXEFMT_LOADER ExeFmtpLoaders[] =
2558 {
2559     PeFmtCreateSection,
2560 #ifdef __ELF
2561     ElfFmtCreateSection
2562 #endif
2563 };
2564 
2565 static
2566 PMM_SECTION_SEGMENT
2567 NTAPI
2568 ExeFmtpAllocateSegments(IN ULONG NrSegments)
2569 {
2570     SIZE_T SizeOfSegments;
2571     PMM_SECTION_SEGMENT Segments;
2572 
2573     /* TODO: check for integer overflow */
2574     SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
2575 
2576     Segments = ExAllocatePoolWithTag(NonPagedPool,
2577                                      SizeOfSegments,
2578                                      TAG_MM_SECTION_SEGMENT);
2579 
2580     if(Segments)
2581         RtlZeroMemory(Segments, SizeOfSegments);
2582 
2583     return Segments;
2584 }
2585 static
2586 NTSTATUS
2587 NTAPI
2588 ExeFmtpReadFile(IN PVOID File,
2589                 IN PLARGE_INTEGER Offset,
2590                 IN ULONG Length,
2591                 OUT PVOID * Data,
2592                 OUT PVOID * AllocBase,
2593                 OUT PULONG ReadSize)
2594 {
2595     NTSTATUS Status;
2596     LARGE_INTEGER FileOffset;
2597     ULONG AdjustOffset;
2598     ULONG OffsetAdjustment;
2599     ULONG BufferSize;
2600     ULONG UsedSize;
2601     PVOID Buffer;
2602     PFILE_OBJECT FileObject = File;
2603     IO_STATUS_BLOCK Iosb;
2604 
2605     ASSERT_IRQL_LESS(DISPATCH_LEVEL);
2606 
2607     if(Length == 0)
2608     {
2609         KeBugCheck(MEMORY_MANAGEMENT);
2610     }
2611 
2612     FileOffset = *Offset;
2613 
2614     /* Negative/special offset: it cannot be used in this context */
2615     if(FileOffset.u.HighPart < 0)
2616     {
2617         KeBugCheck(MEMORY_MANAGEMENT);
2618     }
2619 
2620     AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
2621     OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
2622     FileOffset.u.LowPart = AdjustOffset;
2623 
2624     BufferSize = Length + OffsetAdjustment;
2625     BufferSize = PAGE_ROUND_UP(BufferSize);
2626 
2627     /*
2628      * It's ok to use paged pool, because this is a temporary buffer only used in
2629      * the loading of executables. The assumption is that MmCreateSection is
2630      * always called at low IRQLs and that these buffers don't survive a brief
2631      * initialization phase
2632      */
2633     Buffer = ExAllocatePoolWithTag(PagedPool, BufferSize, 'rXmM');
2634     if (!Buffer)
2635     {
2636         return STATUS_INSUFFICIENT_RESOURCES;
2637     }
2638 
2639     Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
2640 
2641     UsedSize = (ULONG)Iosb.Information;
2642 
2643     if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
2644     {
2645         Status = STATUS_IN_PAGE_ERROR;
2646         ASSERT(!NT_SUCCESS(Status));
2647     }
2648 
2649     if(NT_SUCCESS(Status))
2650     {
2651         *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
2652         *AllocBase = Buffer;
2653         *ReadSize = UsedSize - OffsetAdjustment;
2654     }
2655     else
2656     {
2657         ExFreePoolWithTag(Buffer, 'rXmM');
2658     }
2659 
2660     return Status;
2661 }
2662 
2663 #ifdef NASSERT
2664 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
2665 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
2666 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
2667 #else
2668 static
2669 VOID
2670 NTAPI
2671 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2672 {
2673     ULONG i;
2674 
2675     for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
2676     {
2677         ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2678                ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
2679     }
2680 }
2681 
2682 static
2683 VOID
2684 NTAPI
2685 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2686 {
2687     ULONG i;
2688 
2689     MmspAssertSegmentsSorted(ImageSectionObject);
2690 
2691     for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2692     {
2693         ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
2694 
2695         if(i > 0)
2696         {
2697             ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
2698                    (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2699                     ImageSectionObject->Segments[i - 1].Length.QuadPart));
2700         }
2701     }
2702 }
2703 
2704 static
2705 VOID
2706 NTAPI
2707 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
2708 {
2709     ULONG i;
2710 
2711     for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2712     {
2713         ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
2714         ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
2715     }
2716 }
2717 #endif
2718 
2719 static
2720 int
2721 __cdecl
2722 MmspCompareSegments(const void * x,
2723                     const void * y)
2724 {
2725     const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
2726     const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
2727 
2728     if (Segment1->Image.VirtualAddress > Segment2->Image.VirtualAddress)
2729         return 1;
2730     else if (Segment1->Image.VirtualAddress < Segment2->Image.VirtualAddress)
2731         return -1;
2732     else
2733         return 0;
2734 }
2735 
2736 /*
2737  * Ensures an image section's segments are sorted in memory
2738  */
2739 static
2740 VOID
2741 NTAPI
2742 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2743                  IN ULONG Flags)
2744 {
2745     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
2746     {
2747         MmspAssertSegmentsSorted(ImageSectionObject);
2748     }
2749     else
2750     {
2751         qsort(ImageSectionObject->Segments,
2752               ImageSectionObject->NrSegments,
2753               sizeof(ImageSectionObject->Segments[0]),
2754               MmspCompareSegments);
2755     }
2756 }
2757 
2758 
2759 /*
2760  * Ensures an image section's segments don't overlap in memory and don't have
2761  * gaps and don't have a null size. We let them map to overlapping file regions,
2762  * though - that's not necessarily an error
2763  */
2764 static
2765 BOOLEAN
2766 NTAPI
2767 MmspCheckSegmentBounds
2768 (
2769     IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2770     IN ULONG Flags
2771 )
2772 {
2773     ULONG i;
2774 
2775     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
2776     {
2777         MmspAssertSegmentsNoOverlap(ImageSectionObject);
2778         return TRUE;
2779     }
2780 
2781     ASSERT(ImageSectionObject->NrSegments >= 1);
2782 
2783     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2784     {
2785         if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
2786         {
2787             return FALSE;
2788         }
2789 
2790         if(i > 0)
2791         {
2792             /*
2793              * TODO: relax the limitation on gaps. For example, gaps smaller than a
2794              * page could be OK (Windows seems to be OK with them), and larger gaps
2795              * could lead to image sections spanning several discontiguous regions
2796              * (NtMapViewOfSection could then refuse to map them, and they could
2797              * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
2798              */
2799             if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
2800                     ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
2801                     ImageSectionObject->Segments[i].Image.VirtualAddress)
2802             {
2803                 return FALSE;
2804             }
2805         }
2806     }
2807 
2808     return TRUE;
2809 }
2810 
2811 /*
2812  * Merges and pads an image section's segments until they all are page-aligned
2813  * and have a size that is a multiple of the page size
2814  */
2815 static
2816 BOOLEAN
2817 NTAPI
2818 MmspPageAlignSegments
2819 (
2820     IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
2821     IN ULONG Flags
2822 )
2823 {
2824     ULONG i;
2825     ULONG LastSegment;
2826     PMM_SECTION_SEGMENT EffectiveSegment;
2827 
2828     if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
2829     {
2830         MmspAssertSegmentsPageAligned(ImageSectionObject);
2831         return TRUE;
2832     }
2833 
2834     LastSegment = 0;
2835     EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2836 
2837     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
2838     {
2839         /*
2840          * The first segment requires special handling
2841          */
2842         if (i == 0)
2843         {
2844             ULONG_PTR VirtualAddress;
2845             ULONG_PTR VirtualOffset;
2846 
2847             VirtualAddress = EffectiveSegment->Image.VirtualAddress;
2848 
2849             /* Round down the virtual address to the nearest page */
2850             EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
2851 
2852             /* Round up the virtual size to the nearest page */
2853             EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
2854                                                 EffectiveSegment->Image.VirtualAddress;
2855 
2856             /* Adjust the raw address and size */
2857             VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
2858 
2859             if (EffectiveSegment->Image.FileOffset < VirtualOffset)
2860             {
2861                 return FALSE;
2862             }
2863 
2864             /*
2865              * Garbage in, garbage out: unaligned base addresses make the file
2866              * offset point in curious and odd places, but that's what we were
2867              * asked for
2868              */
2869             EffectiveSegment->Image.FileOffset -= VirtualOffset;
2870             EffectiveSegment->RawLength.QuadPart += VirtualOffset;
2871         }
2872         else
2873         {
2874             PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
2875             ULONG_PTR EndOfEffectiveSegment;
2876 
2877             EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
2878             ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
2879 
2880             /*
2881              * The current segment begins exactly where the current effective
2882              * segment ended, therefore beginning a new effective segment
2883              */
2884             if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
2885             {
2886                 LastSegment ++;
2887                 ASSERT(LastSegment <= i);
2888                 ASSERT(LastSegment < ImageSectionObject->NrSegments);
2889 
2890                 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
2891 
2892                 if (LastSegment != i)
2893                 {
2894                     /*
2895                      * Copy the current segment. If necessary, the effective segment
2896                      * will be expanded later
2897                      */
2898                     *EffectiveSegment = *Segment;
2899                 }
2900 
2901                 /*
2902                  * Page-align the virtual size. We know for sure the virtual address
2903                  * already is
2904                  */
2905                 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
2906                 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
2907             }
2908             /*
2909              * The current segment is still part of the current effective segment:
2910              * extend the effective segment to reflect this
2911              */
2912             else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
2913             {
2914                 static const ULONG FlagsToProtection[16] =
2915                 {
2916                     PAGE_NOACCESS,
2917                     PAGE_READONLY,
2918                     PAGE_READWRITE,
2919                     PAGE_READWRITE,
2920                     PAGE_EXECUTE_READ,
2921                     PAGE_EXECUTE_READ,
2922                     PAGE_EXECUTE_READWRITE,
2923                     PAGE_EXECUTE_READWRITE,
2924                     PAGE_WRITECOPY,
2925                     PAGE_WRITECOPY,
2926                     PAGE_WRITECOPY,
2927                     PAGE_WRITECOPY,
2928                     PAGE_EXECUTE_WRITECOPY,
2929                     PAGE_EXECUTE_WRITECOPY,
2930                     PAGE_EXECUTE_WRITECOPY,
2931                     PAGE_EXECUTE_WRITECOPY
2932                 };
2933 
2934                 unsigned ProtectionFlags;
2935 
2936                 /*
2937                  * Extend the file size
2938                  */
2939 
2940                 /* Unaligned segments must be contiguous within the file */
2941                 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
2942                                                   EffectiveSegment->RawLength.QuadPart))
2943                 {
2944                     return FALSE;
2945                 }
2946 
2947                 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
2948 
2949                 /*
2950                  * Extend the virtual size
2951                  */
2952                 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
2953 
2954                 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
2955                                                     EffectiveSegment->Image.VirtualAddress;
2956 
2957                 /*
2958                  * Merge the protection
2959                  */
2960                 EffectiveSegment->Protection |= Segment->Protection;
2961 
2962                 /* Clean up redundance */
2963                 ProtectionFlags = 0;
2964 
2965                 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
2966                     ProtectionFlags |= 1 << 0;
2967 
2968                 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
2969                     ProtectionFlags |= 1 << 1;
2970 
2971                 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
2972                     ProtectionFlags |= 1 << 2;
2973 
2974                 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2975                     ProtectionFlags |= 1 << 3;
2976 
2977                 ASSERT(ProtectionFlags < 16);
2978                 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
2979 
2980                 /* If a segment was required to be shared and cannot, fail */
2981                 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
2982                         EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
2983                 {
2984                     return FALSE;
2985                 }
2986             }
2987             /*
2988              * We assume no holes between segments at this point
2989              */
2990             else
2991             {
2992                 KeBugCheck(MEMORY_MANAGEMENT);
2993             }
2994         }
2995     }
2996     ImageSectionObject->NrSegments = LastSegment + 1;
2997 
2998     return TRUE;
2999 }
3000 
3001 NTSTATUS
3002 ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,
3003                           PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3004 {
3005     LARGE_INTEGER Offset;
3006     PVOID FileHeader;
3007     PVOID FileHeaderBuffer;
3008     ULONG FileHeaderSize;
3009     ULONG Flags;
3010     ULONG OldNrSegments;
3011     NTSTATUS Status;
3012     ULONG i;
3013 
3014     /*
3015      * Read the beginning of the file (2 pages). Should be enough to contain
3016      * all (or most) of the headers
3017      */
3018     Offset.QuadPart = 0;
3019 
3020     Status = ExeFmtpReadFile (FileObject,
3021                               &Offset,
3022                               PAGE_SIZE * 2,
3023                               &FileHeader,
3024                               &FileHeaderBuffer,
3025                               &FileHeaderSize);
3026 
3027     if (!NT_SUCCESS(Status))
3028         return Status;
3029 
3030     if (FileHeaderSize == 0)
3031     {
3032         ExFreePool(FileHeaderBuffer);
3033         return STATUS_UNSUCCESSFUL;
3034     }
3035 
3036     /*
3037      * Look for a loader that can handle this executable
3038      */
3039     for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3040     {
3041         Flags = 0;
3042 
3043         Status = ExeFmtpLoaders[i](FileHeader,
3044                                    FileHeaderSize,
3045                                    FileObject,
3046                                    ImageSectionObject,
3047                                    &Flags,
3048                                    ExeFmtpReadFile,
3049                                    ExeFmtpAllocateSegments);
3050 
3051         if (!NT_SUCCESS(Status))
3052         {
3053             if (ImageSectionObject->Segments)
3054             {
3055                 ExFreePool(ImageSectionObject->Segments);
3056                 ImageSectionObject->Segments = NULL;
3057             }
3058         }
3059 
3060         if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3061             break;
3062     }
3063 
3064     ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3065 
3066     /*
3067      * No loader handled the format
3068      */
3069     if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3070     {
3071         Status = STATUS_INVALID_IMAGE_NOT_MZ;
3072         ASSERT(!NT_SUCCESS(Status));
3073     }
3074 
3075     if (!NT_SUCCESS(Status))
3076         return Status;
3077 
3078     ASSERT(ImageSectionObject->Segments != NULL);
3079     ASSERT(ImageSectionObject->RefCount > 0);
3080 
3081     /*
3082      * Some defaults
3083      */
3084     /* FIXME? are these values platform-dependent? */
3085     if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3086         ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3087 
3088     if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3089         ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3090 
3091     if(ImageSectionObject->BasedAddress == NULL)
3092     {
3093         if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3094             ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3095         else
3096             ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3097     }
3098 
3099     /*
3100      * And now the fun part: fixing the segments
3101      */
3102 
3103     /* Sort them by virtual address */
3104     MmspSortSegments(ImageSectionObject, Flags);
3105 
3106     /* Ensure they don't overlap in memory */
3107     if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3108         return STATUS_INVALID_IMAGE_FORMAT;
3109 
3110     /* Ensure they are aligned */
3111     OldNrSegments = ImageSectionObject->NrSegments;
3112 
3113     if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3114         return STATUS_INVALID_IMAGE_FORMAT;
3115 
3116     /* Trim them if the alignment phase merged some of them */
3117     if (ImageSectionObject->NrSegments < OldNrSegments)
3118     {
3119         PMM_SECTION_SEGMENT Segments;
3120         SIZE_T SizeOfSegments;
3121 
3122         SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3123 
3124         Segments = ExAllocatePoolWithTag(PagedPool,
3125                                          SizeOfSegments,
3126                                          TAG_MM_SECTION_SEGMENT);
3127 
3128         if (Segments == NULL)
3129             return STATUS_INSUFFICIENT_RESOURCES;
3130 
3131         RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3132         ExFreePool(ImageSectionObject->Segments);
3133         ImageSectionObject->Segments = Segments;
3134     }
3135 
3136     /* And finish their initialization */
3137     for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3138     {
3139         ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3140         ImageSectionObject->Segments[i].ReferenceCount = &ImageSectionObject->RefCount;
3141         ImageSectionObject->Segments[i].Flags = &ImageSectionObject->SegFlags;
3142         MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3143         ImageSectionObject->Segments[i].FileObject = FileObject;
3144     }
3145 
3146     ASSERT(ImageSectionObject->RefCount > 0);
3147 
3148     ImageSectionObject->FileObject = FileObject;
3149 
3150     ASSERT(NT_SUCCESS(Status));
3151     return Status;
3152 }
3153 
3154 NTSTATUS
3155 MmCreateImageSection(PSECTION *SectionObject,
3156                      ACCESS_MASK DesiredAccess,
3157                      POBJECT_ATTRIBUTES ObjectAttributes,
3158                      PLARGE_INTEGER UMaximumSize,
3159                      ULONG SectionPageProtection,
3160                      ULONG AllocationAttributes,
3161                      PFILE_OBJECT FileObject)
3162 {
3163     PSECTION Section;
3164     NTSTATUS Status;
3165     PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3166     KIRQL OldIrql;
3167 
3168 
3169     if (FileObject == NULL)
3170         return STATUS_INVALID_FILE_FOR_SECTION;
3171 
3172     if (FileObject->SectionObjectPointer == NULL)
3173     {
3174         DPRINT1("Denying section creation due to missing cache initialization\n");
3175         return STATUS_INVALID_FILE_FOR_SECTION;
3176     }
3177 
3178     /*
3179      * Create the section
3180      */
3181     Status = ObCreateObject (ExGetPreviousMode(),
3182                              MmSectionObjectType,
3183                              ObjectAttributes,
3184                              ExGetPreviousMode(),
3185                              NULL,
3186                              sizeof(*Section),
3187                              0,
3188                              0,
3189                              (PVOID*)(PVOID)&Section);
3190     if (!NT_SUCCESS(Status))
3191     {
3192         return Status;
3193     }
3194 
3195     /*
3196      * Initialize it
3197      */
3198     RtlZeroMemory(Section, sizeof(*Section));
3199 
3200     /* Mark this as a "ROS" Section */
3201     Section->u.Flags.filler = 1;
3202 
3203     Section->InitialPageProtection = SectionPageProtection;
3204     Section->u.Flags.File = 1;
3205     Section->u.Flags.Image = 1;
3206     if (AllocationAttributes & SEC_NO_CHANGE)
3207         Section->u.Flags.NoChange = 1;
3208 
3209 grab_image_section_object:
3210     OldIrql = MiAcquirePfnLock();
3211 
3212     /* Wait for it to be properly created or deleted */
3213     ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3214     while(ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INDELETE | MM_SEGMENT_INCREATE)))
3215     {
3216         MiReleasePfnLock(OldIrql);
3217 
3218         KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
3219 
3220         OldIrql = MiAcquirePfnLock();
3221         ImageSectionObject = FileObject->SectionObjectPointer->ImageSectionObject;
3222     }
3223 
3224     if (ImageSectionObject == NULL)
3225     {
3226         NTSTATUS StatusExeFmt;
3227 
3228         /* Release the lock because ExAllocatePoolWithTag could need to acquire it */
3229         MiReleasePfnLock(OldIrql);
3230 
3231         ImageSectionObject = ExAllocatePoolZero(NonPagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3232         if (ImageSectionObject == NULL)
3233         {
3234             ObDereferenceObject(Section);
3235             return STATUS_NO_MEMORY;
3236         }
3237 
3238         ImageSectionObject->SegFlags = MM_SEGMENT_INCREATE;
3239         ImageSectionObject->RefCount = 1;
3240         ImageSectionObject->SectionCount = 1;
3241 
3242         OldIrql = MiAcquirePfnLock();
3243         if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
3244         {
3245             MiReleasePfnLock(OldIrql);
3246             /* Bad luck. Start over */
3247             ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3248             goto grab_image_section_object;
3249         }
3250 
3251         FileObject->SectionObjectPointer->ImageSectionObject = ImageSectionObject;
3252 
3253         MiReleasePfnLock(OldIrql);
3254 
3255         /* Purge the cache */
3256         CcFlushCache(FileObject->SectionObjectPointer, NULL, 0, NULL);
3257 
3258         StatusExeFmt = ExeFmtpCreateImageSection(FileObject, ImageSectionObject);
3259 
3260         if (!NT_SUCCESS(StatusExeFmt))
3261         {
3262             /* Unset */
3263             OldIrql = MiAcquirePfnLock();
3264             FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3265             MiReleasePfnLock(OldIrql);
3266 
3267             if(ImageSectionObject->Segments != NULL)
3268                 ExFreePool(ImageSectionObject->Segments);
3269 
3270             /*
3271              * If image file is empty, then return that the file is invalid for section
3272              */
3273             Status = StatusExeFmt;
3274             if (StatusExeFmt == STATUS_END_OF_FILE)
3275             {
3276                 Status = STATUS_INVALID_FILE_FOR_SECTION;
3277             }
3278 
3279             ExFreePoolWithTag(ImageSectionObject, TAG_MM_SECTION_SEGMENT);
3280             ObDereferenceObject(Section);
3281             return Status;
3282         }
3283 
3284         Section->Segment = (PSEGMENT)ImageSectionObject;
3285         ASSERT(ImageSectionObject->Segments);
3286         ASSERT(ImageSectionObject->RefCount > 0);
3287 
3288         /*
3289          * Lock the file
3290          */
3291         Status = MmspWaitForFileLock(FileObject);
3292         if (!NT_SUCCESS(Status))
3293         {
3294             /* Unset */
3295             OldIrql = MiAcquirePfnLock();
3296             FileObject->SectionObjectPointer->ImageSectionObject = NULL;
3297             MiReleasePfnLock(OldIrql);
3298 
3299             ExFreePool(ImageSectionObject->Segments);
3300             ExFreePool(ImageSectionObject);
3301             ObDereferenceObject(Section);
3302             return Status;
3303         }
3304 
3305         OldIrql = MiAcquirePfnLock();
3306         ImageSectionObject->SegFlags &= ~MM_SEGMENT_INCREATE;
3307 
3308         /* Take a ref on the file on behalf of the newly created structure */
3309         ObReferenceObject(FileObject);
3310 
3311         MiReleasePfnLock(OldIrql);
3312 
3313         Status = StatusExeFmt;
3314     }
3315     else
3316     {
3317         /* If FS driver called for delete, tell them it's not possible anymore. */
3318         ImageSectionObject->SegFlags &= ~MM_IMAGE_SECTION_FLUSH_DELETE;
3319 
3320         /* Take one ref */
3321         InterlockedIncrement64(&ImageSectionObject->RefCount);
3322         ImageSectionObject->SectionCount++;
3323 
3324         MiReleasePfnLock(OldIrql);
3325 
3326         Section->Segment = (PSEGMENT)ImageSectionObject;
3327 
3328         Status = STATUS_SUCCESS;
3329     }
3330     //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3331     *SectionObject = Section;
3332     ASSERT(ImageSectionObject->RefCount > 0);
3333 
3334     return Status;
3335 }
3336 
3337 
3338 
3339 static NTSTATUS
3340 MmMapViewOfSegment(
3341     PMMSUPPORT AddressSpace,
3342     BOOLEAN AsImage,
3343     PMM_SECTION_SEGMENT Segment,
3344     PVOID* BaseAddress,
3345     SIZE_T ViewSize,
3346     ULONG Protect,
3347     LONGLONG ViewOffset,
3348     ULONG AllocationType)
3349 {
3350     PMEMORY_AREA MArea;
3351     NTSTATUS Status;
3352     ULONG Granularity;
3353 
3354     ASSERT(ViewSize != 0);
3355 
3356     if (Segment->WriteCopy)
3357     {
3358         /* We have to do this because the not present fault
3359          * and access fault handlers depend on the protection
3360          * that should be granted AFTER the COW fault takes
3361          * place to be in Region->Protect. The not present fault
3362          * handler changes this to the correct protection for COW when
3363          * mapping the pages into the process's address space. If a COW
3364          * fault takes place, the access fault handler sets the page protection
3365          * to these values for the newly copied pages
3366          */
3367         if (Protect == PAGE_WRITECOPY)
3368             Protect = PAGE_READWRITE;
3369         else if (Protect == PAGE_EXECUTE_WRITECOPY)
3370             Protect = PAGE_EXECUTE_READWRITE;
3371     }
3372 
3373     if (*BaseAddress == NULL)
3374         Granularity = MM_ALLOCATION_GRANULARITY;
3375     else
3376         Granularity = PAGE_SIZE;
3377 
3378 #ifdef NEWCC
3379     if (Segment->Flags & MM_DATAFILE_SEGMENT)
3380     {
3381         LARGE_INTEGER FileOffset;
3382         FileOffset.QuadPart = ViewOffset;
3383         ObReferenceObject(Section);
3384         return _MiMapViewOfSegment(AddressSpace, Segment, BaseAddress, ViewSize, Protect, &FileOffset, AllocationType, __FILE__, __LINE__);
3385     }
3386 #endif
3387     Status = MmCreateMemoryArea(AddressSpace,
3388                                 MEMORY_AREA_SECTION_VIEW,
3389                                 BaseAddress,
3390                                 ViewSize,
3391                                 Protect,
3392                                 &MArea,
3393                                 AllocationType,
3394                                 Granularity);
3395     if (!NT_SUCCESS(Status))
3396     {
3397         DPRINT1("Mapping between 0x%p and 0x%p failed (%X).\n",
3398                 (*BaseAddress), (char*)(*BaseAddress) + ViewSize, Status);
3399         return Status;
3400     }
3401 
3402     InterlockedIncrement64(Segment->ReferenceCount);
3403 
3404     MArea->SectionData.Segment = Segment;
3405     MArea->SectionData.ViewOffset = ViewOffset;
3406     if (AsImage)
3407     {
3408         MArea->VadNode.u.VadFlags.VadType = VadImageMap;
3409     }
3410 
3411     MmInitializeRegion(&MArea->SectionData.RegionListHead,
3412                        ViewSize, 0, Protect);
3413 
3414     return STATUS_SUCCESS;
3415 }
3416 
3417 
3418 static VOID
3419 MmFreeSectionPage(PVOID Context, MEMORY_AREA* MemoryArea, PVOID Address,
3420                   PFN_NUMBER Page, SWAPENTRY SwapEntry, BOOLEAN Dirty)
3421 {
3422     ULONG_PTR Entry;
3423     LARGE_INTEGER Offset;
3424     SWAPENTRY SavedSwapEntry;
3425     PMM_SECTION_SEGMENT Segment;
3426     PMMSUPPORT AddressSpace;
3427     PEPROCESS Process;
3428 
3429     AddressSpace = (PMMSUPPORT)Context;
3430     Process = MmGetAddressSpaceOwner(AddressSpace);
3431 
3432     Address = (PVOID)PAGE_ROUND_DOWN(Address);
3433 
3434     Offset.QuadPart = ((ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)) +
3435                       MemoryArea->SectionData.ViewOffset;
3436 
3437     Segment = MemoryArea->SectionData.Segment;
3438 
3439     Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3440     while (Entry && MM_IS_WAIT_PTE(Entry))
3441     {
3442         MmUnlockSectionSegment(Segment);
3443         MmUnlockAddressSpace(AddressSpace);
3444 
3445         YieldProcessor();
3446 
3447         MmLockAddressSpace(AddressSpace);
3448         MmLockSectionSegment(Segment);
3449         Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
3450     }
3451 
3452     /*
3453      * For a dirty, datafile, non-private page, there shoulkd be no swap entry
3454      */
3455     if (*Segment->Flags & MM_DATAFILE_SEGMENT)
3456     {
3457         if (Page == PFN_FROM_SSE(Entry) && Dirty)
3458         {
3459             ASSERT(SwapEntry == 0);
3460         }
3461     }
3462 
3463     if (SwapEntry != 0)
3464     {
3465         /*
3466          * Sanity check
3467          */
3468         MmFreeSwapPage(SwapEntry);
3469     }
3470     else if (Page != 0)
3471     {
3472         if (IS_SWAP_FROM_SSE(Entry) ||
3473                 Page != PFN_FROM_SSE(Entry))
3474         {
3475             ASSERT(Process != NULL);
3476 
3477             /*
3478              * Just dereference private pages
3479              */
3480             SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
3481             if (SavedSwapEntry != 0)
3482             {
3483                 MmFreeSwapPage(SavedSwapEntry);
3484                 MmSetSavedSwapEntryPage(Page, 0);
3485             }
3486             MmDeleteRmap(Page, Process, Address);
3487             MmReleasePageMemoryConsumer(MC_USER, Page);
3488         }
3489         else
3490         {
3491             if (Process)
3492             {
3493                 MmDeleteRmap(Page, Process, Address);
3494             }
3495 
3496             /* We don't dirtify for System Space Maps. We let Cc manage that */
3497             MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset, Process ? Dirty : FALSE, FALSE, NULL);
3498         }
3499     }
3500 }
3501 
3502 static NTSTATUS
3503 MmUnmapViewOfSegment(PMMSUPPORT AddressSpace,
3504                      PVOID BaseAddress)
3505 {
3506     NTSTATUS Status;
3507     PMEMORY_AREA MemoryArea;
3508     PMM_SECTION_SEGMENT Segment;
3509     PLIST_ENTRY CurrentEntry;
3510     PMM_REGION CurrentRegion;
3511     PLIST_ENTRY RegionListHead;
3512 
3513     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3514                  BaseAddress);
3515     if (MemoryArea == NULL)
3516     {
3517         return STATUS_UNSUCCESSFUL;
3518     }
3519 
3520     Segment = MemoryArea->SectionData.Segment;
3521 
3522 #ifdef NEWCC
3523     if (Segment->Flags & MM_DATAFILE_SEGMENT)
3524     {
3525         MmUnlockAddressSpace(AddressSpace);
3526         Status = MmUnmapViewOfCacheSegment(AddressSpace, BaseAddress);
3527         MmLockAddressSpace(AddressSpace);
3528 
3529         return Status;
3530     }
3531 #endif
3532 
3533     MemoryArea->DeleteInProgress = TRUE;
3534 
3535     MmLockSectionSegment(Segment);
3536 
3537     RegionListHead = &MemoryArea->SectionData.RegionListHead;
3538     while (!IsListEmpty(RegionListHead))
3539     {
3540         CurrentEntry = RemoveHeadList(RegionListHead);
3541         CurrentRegion = CONTAINING_RECORD(CurrentEntry, MM_REGION, RegionListEntry);
3542         ExFreePoolWithTag(CurrentRegion, TAG_MM_REGION);
3543     }
3544 
3545     if ((*Segment->Flags) & MM_PHYSICALMEMORY_SEGMENT)
3546     {
3547         Status = MmFreeMemoryArea(AddressSpace,
3548                                   MemoryArea,
3549                                   NULL,
3550                                   NULL);
3551     }
3552     else
3553     {
3554         Status = MmFreeMemoryArea(AddressSpace,
3555                                   MemoryArea,
3556                                   MmFreeSectionPage,
3557                                   AddressSpace);
3558     }
3559     MmUnlockSectionSegment(Segment);
3560     MmDereferenceSegment(Segment);
3561     return Status;
3562 }
3563 
3564 /* This functions must be called with a locked address space */
3565 NTSTATUS
3566 NTAPI
3567 MiRosUnmapViewOfSection(IN PEPROCESS Process,
3568                         IN PVOID BaseAddress,
3569                         IN BOOLEAN SkipDebuggerNotify)
3570 {
3571     NTSTATUS Status;
3572     PMEMORY_AREA MemoryArea;
3573     PMMSUPPORT AddressSpace;
3574     PVOID ImageBaseAddress = 0;
3575 
3576     DPRINT("Opening memory area Process %p BaseAddress %p\n",
3577            Process, BaseAddress);
3578 
3579     ASSERT(Process);
3580 
3581     AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
3582 
3583     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace,
3584                  BaseAddress);
3585     if (MemoryArea == NULL ||
3586 #ifdef NEWCC
3587             ((MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) && (MemoryArea->Type != MEMORY_AREA_CACHE)) ||
3588 #else
3589             (MemoryArea->Type != MEMORY_AREA_SECTION_VIEW) ||
3590 #endif
3591             MemoryArea->DeleteInProgress)
3592 
3593     {
3594         if (MemoryArea) ASSERT(MemoryArea->Type != MEMORY_AREA_OWNED_BY_ARM3);
3595 
3596         DPRINT1("Unable to find memory area at address %p.\n", BaseAddress);
3597         return STATUS_NOT_MAPPED_VIEW;
3598     }
3599 
3600     if (MemoryArea->VadNode.u.VadFlags.VadType == VadImageMap)
3601     {
3602         ULONG i;
3603         ULONG NrSegments;
3604         PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3605         PMM_SECTION_SEGMENT SectionSegments;
3606         PMM_SECTION_SEGMENT Segment;
3607 
3608         Segment = MemoryArea->SectionData.Segment;
3609         ImageSectionObject = ImageSectionObjectFromSegment(Segment);
3610         SectionSegments = ImageSectionObject->Segments;
3611         NrSegments = ImageSectionObject->NrSegments;
3612 
3613         MemoryArea->DeleteInProgress = TRUE;
3614 
3615         /* Search for the current segment within the section segments
3616          * and calculate the image base address */
3617         for (i = 0; i < NrSegments; i++)
3618         {
3619             if (Segment == &SectionSegments[i])
3620             {
3621                 ImageBaseAddress = (char*)BaseAddress - (ULONG_PTR)SectionSegments[i].Image.VirtualAddress;
3622                 break;
3623             }
3624         }
3625         if (i >= NrSegments)
3626         {
3627             KeBugCheck(MEMORY_MANAGEMENT);
3628         }
3629 
3630         for (i = 0; i < NrSegments; i++)
3631         {
3632             PVOID SBaseAddress = (PVOID)
3633                                  ((char*)ImageBaseAddress + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
3634 
3635             Status = MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
3636             if (!NT_SUCCESS(Status))
3637             {
3638                 DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3639                         SBaseAddress, Process, Status);
3640                 ASSERT(NT_SUCCESS(Status));
3641             }
3642         }
3643         DPRINT("One mapping less for %p\n", ImageSectionObject->FileObject->SectionObjectPointer);
3644         InterlockedDecrement(&ImageSectionObject->MapCount);
3645     }
3646     else
3647     {
3648         Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3649         if (!NT_SUCCESS(Status))
3650         {
3651             DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3652                     BaseAddress, Process, Status);
3653             ASSERT(NT_SUCCESS(Status));
3654         }
3655     }
3656 
3657     /* Notify debugger */
3658     if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3659 
3660     return STATUS_SUCCESS;
3661 }
3662 
3663 
3664 
3665 
3666 /**
3667  * Queries the information of a section object.
3668  *
3669  * @param SectionHandle
3670  *        Handle to the section object. It must be opened with SECTION_QUERY
3671  *        access.
3672  * @param SectionInformationClass
3673  *        Index to a certain information structure. Can be either
3674  *        SectionBasicInformation or SectionImageInformation. The latter
3675  *        is valid only for sections that were created with the SEC_IMAGE
3676  *        flag.
3677  * @param SectionInformation
3678  *        Caller supplies storage for resulting information.
3679  * @param Length
3680  *        Size of the supplied storage.
3681  * @param ResultLength
3682  *        Data written.
3683  *
3684  * @return Status.
3685  *
3686  * @implemented
3687  */
3688 NTSTATUS
3689 NTAPI
3690 NtQuerySection(
3691     _In_ HANDLE SectionHandle,
3692     _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3693     _Out_ PVOID SectionInformation,
3694     _In_ SIZE_T SectionInformationLength,
3695     _Out_opt_ PSIZE_T ResultLength)
3696 {
3697     PSECTION Section;
3698     KPROCESSOR_MODE PreviousMode;
3699     NTSTATUS Status;
3700     PAGED_CODE();
3701 
3702     PreviousMode = ExGetPreviousMode();
3703     if (PreviousMode != KernelMode)
3704     {
3705         _SEH2_TRY
3706         {
3707             ProbeForWrite(SectionInformation,
3708                           SectionInformationLength,
3709                           __alignof(ULONG));
3710             if (ResultLength != NULL)
3711             {
3712                 ProbeForWrite(ResultLength,
3713                               sizeof(*ResultLength),
3714                               __alignof(SIZE_T));
3715             }
3716         }
3717         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3718         {
3719             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3720         }
3721         _SEH2_END;
3722     }
3723 
3724     if (SectionInformationClass == SectionBasicInformation)
3725     {
3726         if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3727         {
3728             return STATUS_INFO_LENGTH_MISMATCH;
3729         }
3730     }
3731     else if (SectionInformationClass == SectionImageInformation)
3732     {
3733         if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3734         {
3735             return STATUS_INFO_LENGTH_MISMATCH;
3736         }
3737     }
3738     else
3739     {
3740         return STATUS_INVALID_INFO_CLASS;
3741     }
3742 
3743     Status = ObReferenceObjectByHandle(SectionHandle,
3744                                        SECTION_QUERY,
3745                                        MmSectionObjectType,
3746                                        PreviousMode,
3747                                        (PVOID*)(PVOID)&Section,
3748                                        NULL);
3749     if (!NT_SUCCESS(Status))
3750     {
3751         DPRINT1("Failed to reference section: 0x%lx\n", Status);
3752         return Status;
3753     }
3754 
3755     switch(SectionInformationClass)
3756     {
3757         case SectionBasicInformation:
3758         {
3759             SECTION_BASIC_INFORMATION Sbi;
3760 
3761             Sbi.Size = Section->SizeOfSection;
3762             Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3763 
3764             Sbi.Attributes = 0;
3765             if (Section->u.Flags.File)
3766                 Sbi.Attributes |= SEC_FILE;
3767             if (Section->u.Flags.Image)
3768                 Sbi.Attributes |= SEC_IMAGE;
3769 
3770             /* Those are not set *************
3771             if (Section->u.Flags.Commit)
3772                 Sbi.Attributes |= SEC_COMMIT;
3773             if (Section->u.Flags.Reserve)
3774                 Sbi.Attributes |= SEC_RESERVE;
3775             **********************************/
3776 
3777             if (Section->u.Flags.Image)
3778             {
3779                 if (MiIsRosSectionObject(Section))
3780                 {
3781                     PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3782                     Sbi.BaseAddress = 0;
3783                     Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3784                 }
3785                 else
3786                 {
3787                     /* Not supported yet */
3788                     ASSERT(FALSE);
3789                 }
3790             }
3791             else if (MiIsRosSectionObject(Section))
3792             {
3793                 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3794                 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3795             }
3796             else
3797             {
3798                 DPRINT1("Unimplemented code path!");
3799             }
3800 
3801             _SEH2_TRY
3802             {
3803                 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3804                 if (ResultLength != NULL)
3805                 {
3806                     *ResultLength = sizeof(Sbi);
3807                 }
3808             }
3809             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3810             {
3811                 Status = _SEH2_GetExceptionCode();
3812             }
3813             _SEH2_END;
3814             break;
3815         }
3816         case SectionImageInformation:
3817         {
3818             if (!Section->u.Flags.Image)
3819             {
3820                 Status = STATUS_SECTION_NOT_IMAGE;
3821             }
3822             else if (MiIsRosSectionObject(Section))
3823             {
3824                 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3825 
3826                 _SEH2_TRY
3827                 {
3828                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3829                     *Sii = ImageSectionObject->ImageInformation;
3830                     if (ResultLength != NULL)
3831                     {
3832                         *ResultLength = sizeof(*Sii);
3833                     }
3834                 }
3835                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3836                 {
3837                     Status = _SEH2_GetExceptionCode();
3838                 }
3839                 _SEH2_END;
3840             }
3841             else
3842             {
3843                 _SEH2_TRY
3844                 {
3845                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3846                     *Sii = *Section->Segment->u2.ImageInformation;
3847                     if (ResultLength != NULL)
3848                         *ResultLength = sizeof(*Sii);
3849                 }
3850                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3851                 {
3852                     Status = _SEH2_GetExceptionCode();
3853                 }
3854                 _SEH2_END;
3855             }
3856             break;
3857         }
3858         default:
3859             DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3860             Status = STATUS_NOT_SUPPORTED;
3861     }
3862 
3863     ObDereferenceObject(Section);
3864 
3865     return Status;
3866 }
3867 
3868 /**********************************************************************
3869  * NAME       EXPORTED
3870  * MmMapViewOfSection
3871  *
3872  * DESCRIPTION
3873  * Maps a view of a section into the virtual address space of a
3874  * process.
3875  *
3876  * ARGUMENTS
3877  * Section
3878  *  Pointer to the section object.
3879  *
3880  * ProcessHandle
3881  *  Pointer to the process.
3882  *
3883  * BaseAddress
3884  *  Desired base address (or NULL) on entry;
3885  *  Actual base address of the view on exit.
3886  *
3887  * ZeroBits
3888  *  Number of high order address bits that must be zero.
3889  *
3890  * CommitSize
3891  *  Size in bytes of the initially committed section of
3892  *  the view.
3893  *
3894  * SectionOffset
3895  *  Offset in bytes from the beginning of the section
3896  *  to the beginning of the view.
3897  *
3898  * ViewSize
3899  *  Desired length of map (or zero to map all) on entry
3900  *  Actual length mapped on exit.
3901  *
3902  * InheritDisposition
3903  *  Specified how the view is to be shared with
3904  *  child processes.
3905  *
3906  * AllocationType
3907  *  Type of allocation for the pages.
3908  *
3909  * Protect
3910  *  Protection for the committed region of the view.
3911  *
3912  * RETURN VALUE
3913  * Status.
3914  *
3915  * @implemented
3916  */
3917 NTSTATUS NTAPI
3918 MmMapViewOfSection(IN PVOID SectionObject,
3919                    IN PEPROCESS Process,
3920                    IN OUT PVOID *BaseAddress,
3921                    IN ULONG_PTR ZeroBits,
3922                    IN SIZE_T CommitSize,
3923                    IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3924                    IN OUT PSIZE_T ViewSize,
3925                    IN SECTION_INHERIT InheritDisposition,
3926                    IN ULONG AllocationType,
3927                    IN ULONG Protect)
3928 {
3929     PSECTION Section;
3930     PMMSUPPORT AddressSpace;
3931     NTSTATUS Status = STATUS_SUCCESS;
3932     BOOLEAN NotAtBase = FALSE;
3933 
3934     if (MiIsRosSectionObject(SectionObject) == FALSE)
3935     {
3936         DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
3937         return MmMapViewOfArm3Section(SectionObject,
3938                                       Process,
3939                                       BaseAddress,
3940                                       ZeroBits,
3941                                       CommitSize,
3942                                       SectionOffset,
3943                                       ViewSize,
3944                                       InheritDisposition,
3945                                       AllocationType,
3946                                       Protect);
3947     }
3948 
3949     ASSERT(Process);
3950 
3951     if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION)
3952     {
3953         return STATUS_INVALID_PAGE_PROTECTION;
3954     }
3955 
3956     /* FIXME: We should keep this, but it would break code checking equality */
3957     Protect &= ~PAGE_NOCACHE;
3958 
3959     Section = SectionObject;
3960     AddressSpace = &Process->Vm;
3961 
3962     if (Section->u.Flags.NoChange)
3963         AllocationType |= SEC_NO_CHANGE;
3964 
3965     MmLockAddressSpace(AddressSpace);
3966 
3967     if (Section->u.Flags.Image)
3968     {
3969         ULONG i;
3970         ULONG NrSegments;
3971         ULONG_PTR ImageBase;
3972         SIZE_T ImageSize;
3973         PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3974         PMM_SECTION_SEGMENT SectionSegments;
3975 
3976         ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3977         SectionSegments = ImageSectionObject->Segments;
3978         NrSegments = ImageSectionObject->NrSegments;
3979 
3980         ASSERT(ImageSectionObject->RefCount > 0);
3981 
3982         ImageBase = (ULONG_PTR)*BaseAddress;
3983         if (ImageBase == 0)
3984         {
3985             ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
3986         }
3987 
3988         ImageSize = 0;
3989         for (i = 0; i < NrSegments; i++)
3990         {
3991             ULONG_PTR MaxExtent;
3992             MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
3993                                     SectionSegments[i].Length.QuadPart);
3994             ImageSize = max(ImageSize, MaxExtent);
3995         }
3996 
3997         ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
3998 
3999         /* Check for an illegal base address */
4000         if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
4001                 ((ImageBase + ImageSize) < ImageSize))
4002         {
4003             ASSERT(*BaseAddress == NULL);
4004             ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
4005                                       MM_VIRTMEM_GRANULARITY);
4006             NotAtBase = TRUE;
4007         }
4008         else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
4009         {
4010             ASSERT(*BaseAddress == NULL);
4011             ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4012             NotAtBase = TRUE;
4013         }
4014 
4015         /* Check there is enough space to map the section at that point. */
4016         if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4017                                        PAGE_ROUND_UP(ImageSize)) != NULL)
4018         {
4019             /* Fail if the user requested a fixed base address. */
4020             if ((*BaseAddress) != NULL)
4021             {
4022                 MmUnlockAddressSpace(AddressSpace);
4023                 return STATUS_CONFLICTING_ADDRESSES;
4024             }
4025             /* Otherwise find a gap to map the image. */
4026             ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE);
4027             if (ImageBase == 0)
4028             {
4029                 MmUnlockAddressSpace(AddressSpace);
4030                 return STATUS_CONFLICTING_ADDRESSES;
4031             }
4032             /* Remember that we loaded image at a different base address */
4033             NotAtBase = TRUE;
4034         }
4035 
4036         for (i = 0; i < NrSegments; i++)
4037         {
4038             PVOID SBaseAddress = (PVOID)
4039                                  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4040             MmLockSectionSegment(&SectionSegments[i]);
4041             Status = MmMapViewOfSegment(AddressSpace,
4042                                         TRUE,
4043                                         &SectionSegments[i],
4044                                         &SBaseAddress,
4045                                         SectionSegments[i].Length.QuadPart,
4046                                         SectionSegments[i].Protection,
4047                                         0,
4048                                         0);
4049             MmUnlockSectionSegment(&SectionSegments[i]);
4050             if (!NT_SUCCESS(Status))
4051             {
4052                 /* roll-back */
4053                 while (i--)
4054                 {
4055                     SBaseAddress =  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4056                     MmLockSectionSegment(&SectionSegments[i]);
4057                     MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4058                     MmUnlockSectionSegment(&SectionSegments[i]);
4059                 }
4060 
4061                 MmUnlockAddressSpace(AddressSpace);
4062                 return Status;
4063             }
4064         }
4065 
4066         *BaseAddress = (PVOID)ImageBase;
4067         *ViewSize = ImageSize;
4068 
4069         DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4070 
4071         /* One more map */
4072         InterlockedIncrement(&ImageSectionObject->MapCount);
4073     }
4074     else
4075     {
4076         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4077         LONGLONG ViewOffset;
4078 
4079         ASSERT(Segment->RefCount > 0);
4080 
4081         /* check for write access */
4082         if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
4083                 !(Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)))
4084         {
4085             MmUnlockAddressSpace(AddressSpace);
4086             return STATUS_SECTION_PROTECTION;
4087         }
4088         /* check for read access */
4089         if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) &&
4090                 !(Section->InitialPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4091         {
4092             MmUnlockAddressSpace(AddressSpace);
4093             return STATUS_SECTION_PROTECTION;
4094         }
4095         /* check for execute access */
4096         if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) &&
4097                 !(Section->InitialPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4098         {
4099             MmUnlockAddressSpace(AddressSpace);
4100             return STATUS_SECTION_PROTECTION;
4101         }
4102 
4103         if (SectionOffset == NULL)
4104         {
4105             ViewOffset = 0;
4106         }
4107         else
4108         {
4109             ViewOffset = SectionOffset->QuadPart;
4110         }
4111 
4112         if ((ViewOffset % PAGE_SIZE) != 0)
4113         {
4114             MmUnlockAddressSpace(AddressSpace);
4115             return STATUS_MAPPED_ALIGNMENT;
4116         }
4117 
4118         if ((*ViewSize) == 0)
4119         {
4120             (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4121         }
4122         else if ((ExGetPreviousMode() == UserMode) &&
4123             (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4124             (!Section->u.Flags.Reserve))
4125         {
4126             /* Dubious */
4127             (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4128         }
4129 
4130         *ViewSize = PAGE_ROUND_UP(*ViewSize);
4131 
4132         MmLockSectionSegment(Segment);
4133         Status = MmMapViewOfSegment(AddressSpace,
4134                                     FALSE,
4135                                     Segment,
4136                                     BaseAddress,
4137                                     *ViewSize,
4138                                     Protect,
4139                                     ViewOffset,
4140                                     AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
4141         MmUnlockSectionSegment(Segment);
4142         if (!NT_SUCCESS(Status))
4143         {
4144             MmUnlockAddressSpace(AddressSpace);
4145             return Status;
4146         }
4147     }
4148 
4149     MmUnlockAddressSpace(AddressSpace);
4150 
4151     if (NotAtBase)
4152         Status = STATUS_IMAGE_NOT_AT_BASE;
4153     else
4154         Status = STATUS_SUCCESS;
4155 
4156     return Status;
4157 }
4158 
4159 /*
4160  * @unimplemented
4161  */
4162 BOOLEAN NTAPI
4163 MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4164                       IN PLARGE_INTEGER   NewFileSize)
4165 {
4166     BOOLEAN Ret;
4167     PMM_SECTION_SEGMENT Segment;
4168 
4169     /* Check whether an ImageSectionObject exists */
4170     if (SectionObjectPointer->ImageSectionObject != NULL)
4171     {
4172         DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4173         return FALSE;
4174     }
4175 
4176     Segment = MiGrabDataSection(SectionObjectPointer);
4177     if (!Segment)
4178     {
4179         /* There is no data section. It's fine to do anything. */
4180         return TRUE;
4181     }
4182 
4183     MmLockSectionSegment(Segment);
4184     if ((Segment->SectionCount == 0) ||
4185         ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4186     {
4187         /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4188         Ret = TRUE;
4189     }
4190     else
4191     {
4192         /* We can't shrink, but we can extend */
4193         Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4194 #if DBG
4195         if (!Ret)
4196         {
4197             DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4198         }
4199 #endif
4200     }
4201     MmUnlockSectionSegment(Segment);
4202     MmDereferenceSegment(Segment);
4203 
4204     DPRINT("FIXME: didn't check for outstanding write probes\n");
4205 
4206     return Ret;
4207 }
4208 
4209 static
4210 BOOLEAN
4211 MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment)
4212 {
4213     PCACHE_SECTION_PAGE_TABLE PageTable;
4214 
4215     MmLockSectionSegment(Segment);
4216 
4217     /* Loop over all entries */
4218     for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4219          PageTable != NULL;
4220          PageTable = RtlEnumerateGenericTable(&Segment->PageTable, FALSE))
4221     {
4222         for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4223         {
4224             ULONG_PTR Entry = PageTable->PageEntries[i];
4225             LARGE_INTEGER Offset;
4226 
4227             if (!Entry)
4228                 continue;
4229 
4230             if (IS_SWAP_FROM_SSE(Entry) || (SHARE_COUNT_FROM_SSE(Entry) > 0))
4231             {
4232                 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4233                 MmUnlockSectionSegment(Segment);
4234                 return FALSE;
4235             }
4236 
4237             /* Regular entry */
4238             ASSERT(!IS_WRITE_SSE(Entry));
4239             ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0);
4240 
4241             /* Properly remove using the used API */
4242             Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4243             MmSetPageEntrySectionSegment(Segment, &Offset, 0);
4244             MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4245         }
4246     }
4247 
4248     MmUnlockSectionSegment(Segment);
4249 
4250     return TRUE;
4251 }
4252 
4253 /*
4254  * @implemented
4255  */
4256 BOOLEAN NTAPI
4257 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4258                      IN MMFLUSH_TYPE FlushType)
4259 {
4260     switch(FlushType)
4261     {
4262         case MmFlushForDelete:
4263         {
4264             /*
4265              * FIXME: Check for outstanding write probes on Data section.
4266              * How do we do that ?
4267              */
4268         }
4269         /* Fall-through */
4270         case MmFlushForWrite:
4271         {
4272             KIRQL OldIrql = MiAcquirePfnLock();
4273             PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4274 
4275             DPRINT("Deleting or modifying %p\n", SectionObjectPointer);
4276 
4277             /* Wait for concurrent creation or deletion of image to be done */
4278             ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4279             while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE)))
4280             {
4281                 MiReleasePfnLock(OldIrql);
4282                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4283                 OldIrql = MiAcquirePfnLock();
4284                 ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4285             }
4286 
4287             if (!ImageSectionObject)
4288             {
4289                 DPRINT("No image section object. Accepting\n");
4290                 /* Nothing to do */
4291                 MiReleasePfnLock(OldIrql);
4292                 return TRUE;
4293             }
4294 
4295             /* Do we have open sections or mappings on it ? */
4296             if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount))
4297             {
4298                 /* We do. No way to delete it */
4299                 MiReleasePfnLock(OldIrql);
4300                 DPRINT("Denying. There are mappings open\n");
4301                 return FALSE;
4302             }
4303 
4304             /* There are no sections open on it, but we must still have pages around. Discard everything */
4305             ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
4306             InterlockedIncrement64(&ImageSectionObject->RefCount);
4307             MiReleasePfnLock(OldIrql);
4308 
4309             DPRINT("Purging\n");
4310 
4311             for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
4312             {
4313                 if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i]))
4314                     break;
4315             }
4316 
4317             /* Grab lock again */
4318             OldIrql = MiAcquirePfnLock();
4319 
4320             if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE))
4321             {
4322                 /*
4323                  * Someone actually created a section while we were not looking.
4324                  * Drop our ref and deny.
4325                  * MmDereferenceSegmentWithLock releases Pfn lock
4326                  */
4327                 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4328                 return FALSE;
4329             }
4330 
4331             /* We should be the last one holding a ref here. */
4332             ASSERT(ImageSectionObject->RefCount == 1);
4333             ASSERT(ImageSectionObject->SectionCount == 0);
4334 
4335             /* Dereference the first segment, this will free everything & release the lock */
4336             MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4337             return TRUE;
4338         }
4339     }
4340     return FALSE;
4341 }
4342 
4343 /*
4344  * @implemented
4345  */
4346 NTSTATUS
4347 NTAPI
4348 MmMapViewInSystemSpace (IN PVOID SectionObject,
4349                         OUT PVOID * MappedBase,
4350                         IN OUT PSIZE_T ViewSize)
4351 {
4352     LARGE_INTEGER SectionOffset;
4353 
4354     SectionOffset.QuadPart = 0;
4355 
4356     return MmMapViewInSystemSpaceEx(SectionObject, MappedBase, ViewSize, &SectionOffset, 0);
4357 }
4358 
4359 NTSTATUS
4360 NTAPI
4361 MmMapViewInSystemSpaceEx (
4362     _In_ PVOID SectionObject,
4363     _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase,
4364     _Inout_ PSIZE_T ViewSize,
4365     _Inout_ PLARGE_INTEGER SectionOffset,
4366     _In_ ULONG_PTR Flags
4367     )
4368 {
4369     PSECTION Section = SectionObject;
4370     PMM_SECTION_SEGMENT Segment;
4371     PMMSUPPORT AddressSpace;
4372     NTSTATUS Status;
4373 
4374     UNREFERENCED_PARAMETER(Flags);
4375 
4376     PAGED_CODE();
4377 
4378     if (MiIsRosSectionObject(SectionObject) == FALSE)
4379     {
4380         return MiMapViewInSystemSpace(SectionObject,
4381                                       &MmSession,
4382                                       MappedBase,
4383                                       ViewSize,
4384                                       SectionOffset);
4385     }
4386 
4387     DPRINT("MmMapViewInSystemSpaceEx() called\n");
4388 
4389     /* unsupported for now */
4390     ASSERT(Section->u.Flags.Image == 0);
4391 
4392     Section = SectionObject;
4393     Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4394 
4395     if (*ViewSize == 0)
4396     {
4397         LONGLONG MapSizeLL;
4398 
4399         /* Page-align the mapping */
4400         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4401 
4402         if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4403             return STATUS_INVALID_VIEW_SIZE;
4404 
4405         if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4406             return STATUS_INVALID_VIEW_SIZE;
4407     }
4408     else
4409     {
4410         LONGLONG HelperLL;
4411 
4412         /* Get the map end */
4413         if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4414             return STATUS_INVALID_VIEW_SIZE;
4415 
4416         /* Round it up, if needed */
4417         if (HelperLL % PAGE_SIZE)
4418         {
4419             if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4420                 return STATUS_INVALID_VIEW_SIZE;
4421         }
4422 
4423         /* Now that we have the mapping end, we can align down its start */
4424         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4425 
4426         /* Get the new size */
4427         if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4428             return STATUS_INVALID_VIEW_SIZE;
4429 
4430         if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4431             return STATUS_INVALID_VIEW_SIZE;
4432     }
4433 
4434     AddressSpace = MmGetKernelAddressSpace();
4435 
4436     MmLockAddressSpace(AddressSpace);
4437 
4438     MmLockSectionSegment(Segment);
4439 
4440     Status = MmMapViewOfSegment(AddressSpace,
4441                                 Section->u.Flags.Image,
4442                                 Segment,
4443                                 MappedBase,
4444                                 *ViewSize,
4445                                 PAGE_READWRITE,
4446                                 SectionOffset->QuadPart,
4447                                 SEC_RESERVE);
4448 
4449     MmUnlockSectionSegment(Segment);
4450     MmUnlockAddressSpace(AddressSpace);
4451 
4452     return Status;
4453 }
4454 
4455 /* This function must be called with adress space lock held */
4456 NTSTATUS
4457 NTAPI
4458 MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)
4459 {
4460     DPRINT("MmUnmapViewInSystemSpace() called\n");
4461 
4462     return MmUnmapViewOfSegment(MmGetKernelAddressSpace(), MappedBase);
4463 }
4464 
4465 /**********************************************************************
4466  * NAME       EXPORTED
4467  *  MmCreateSection@
4468  *
4469  * DESCRIPTION
4470  *  Creates a section object.
4471  *
4472  * ARGUMENTS
4473  * SectionObject (OUT)
4474  *  Caller supplied storage for the resulting pointer
4475  *  to a SECTION_OBJECT instance;
4476  *
4477  * DesiredAccess
4478  *  Specifies the desired access to the section can be a
4479  *  combination of:
4480  *   STANDARD_RIGHTS_REQUIRED |
4481  *   SECTION_QUERY   |
4482  *   SECTION_MAP_WRITE  |
4483  *   SECTION_MAP_READ  |
4484  *   SECTION_MAP_EXECUTE
4485  *
4486  * ObjectAttributes [OPTIONAL]
4487  *  Initialized attributes for the object can be used
4488  *  to create a named section;
4489  *
4490  * MaximumSize
4491  *  Maximizes the size of the memory section. Must be
4492  *  non-NULL for a page-file backed section.
4493  *  If value specified for a mapped file and the file is
4494  *  not large enough, file will be extended.
4495  *
4496  * SectionPageProtection
4497  *  Can be a combination of:
4498  *   PAGE_READONLY |
4499  *   PAGE_READWRITE |
4500  *   PAGE_WRITEONLY |
4501  *   PAGE_WRITECOPY
4502  *
4503  * AllocationAttributes
4504  *  Can be a combination of:
4505  *   SEC_IMAGE |
4506  *   SEC_RESERVE
4507  *
4508  * FileHandle
4509  *  Handle to a file to create a section mapped to a file
4510  *  instead of a memory backed section;
4511  *
4512  * File
4513  *  Unknown.
4514  *
4515  * RETURN VALUE
4516  *  Status.
4517  *
4518  * @implemented
4519  */
4520 NTSTATUS NTAPI
4521 MmCreateSection (OUT PVOID  * Section,
4522                  IN ACCESS_MASK  DesiredAccess,
4523                  IN POBJECT_ATTRIBUTES ObjectAttributes     OPTIONAL,
4524                  IN PLARGE_INTEGER  MaximumSize,
4525                  IN ULONG   SectionPageProtection,
4526                  IN ULONG   AllocationAttributes,
4527                  IN HANDLE   FileHandle   OPTIONAL,
4528                  IN PFILE_OBJECT  FileObject  OPTIONAL)
4529 {
4530     NTSTATUS Status;
4531     ULONG Protection;
4532     PSECTION *SectionObject = (PSECTION *)Section;
4533     BOOLEAN FileLock = FALSE;
4534 
4535     /* Check if an ARM3 section is being created instead */
4536     if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY)))
4537     {
4538         if (!(FileObject) && !(FileHandle))
4539         {
4540             return MmCreateArm3Section(Section,
4541                                        DesiredAccess,
4542                                        ObjectAttributes,
4543                                        MaximumSize,
4544                                        SectionPageProtection,
4545                                        AllocationAttributes &~ 1,
4546                                        FileHandle,
4547                                        FileObject);
4548         }
4549     }
4550 
4551     /* Convert section flag to page flag */
4552     if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
4553 
4554     /* Check to make sure the protection is correct. Nt* does this already */
4555     Protection = MiMakeProtectionMask(SectionPageProtection);
4556     if (Protection == MM_INVALID_PROTECTION)
4557     {
4558         DPRINT1("Page protection is invalid\n");
4559         return STATUS_INVALID_PAGE_PROTECTION;
4560     }
4561 
4562     /* Check if this is going to be a data or image backed file section */
4563     if ((FileHandle) || (FileObject))
4564     {
4565         /* These cannot be mapped with large pages */
4566         if (AllocationAttributes & SEC_LARGE_PAGES)
4567         {
4568             DPRINT1("Large pages cannot be used with an image mapping\n");
4569             return STATUS_INVALID_PARAMETER_6;
4570         }
4571 
4572         /* Did the caller pass a file object ? */
4573         if (FileObject)
4574         {
4575             /* Reference the object directly */
4576             ObReferenceObject(FileObject);
4577 
4578             /* We don't create image mappings with file objects */
4579             AllocationAttributes &= ~SEC_IMAGE;
4580         }
4581         else
4582         {
4583             /* Reference the file handle to get the object */
4584             Status = ObReferenceObjectByHandle(FileHandle,
4585                                                MmMakeFileAccess[Protection],
4586                                                IoFileObjectType,
4587                                                ExGetPreviousMode(),
4588                                                (PVOID*)&FileObject,
4589                                                NULL);
4590             if (!NT_SUCCESS(Status))
4591             {
4592                 DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4593                 return Status;
4594             }
4595 
4596             /* Lock the file */
4597             Status = FsRtlAcquireToCreateMappedSection(FileObject, SectionPageProtection);
4598             if (!NT_SUCCESS(Status))
4599             {
4600                 ObDereferenceObject(FileObject);
4601                 return Status;
4602             }
4603 
4604             FileLock = TRUE;
4605 
4606             /* Deny access if there are writes on the file */
4607 #if 0
4608             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4609             {
4610                 DPRINT1("Cannot create image maps with writers open on the file!\n");
4611                 Status = STATUS_ACCESS_DENIED;
4612                 goto Quit;
4613             }
4614 #else
4615             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4616                 DPRINT1("Creating image map with writers open on the file!\n");
4617 #endif
4618         }
4619     }
4620     else
4621     {
4622         /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4623         if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
4624     }
4625 
4626     if (AllocationAttributes & SEC_IMAGE)
4627     {
4628         Status = MmCreateImageSection(SectionObject,
4629                                       DesiredAccess,
4630                                       ObjectAttributes,
4631                                       MaximumSize,
4632                                       SectionPageProtection,
4633                                       AllocationAttributes,
4634                                       FileObject);
4635     }
4636 #ifndef NEWCC
4637     else if (FileObject != NULL)
4638     {
4639         Status =  MmCreateDataFileSection(SectionObject,
4640                                           DesiredAccess,
4641                                           ObjectAttributes,
4642                                           MaximumSize,
4643                                           SectionPageProtection,
4644                                           AllocationAttributes,
4645                                           FileObject,
4646                                           FileHandle != NULL);
4647     }
4648 #else
4649     else if (FileHandle != NULL || FileObject != NULL)
4650     {
4651         Status = MmCreateCacheSection(SectionObject,
4652                                       DesiredAccess,
4653                                       ObjectAttributes,
4654                                       MaximumSize,
4655                                       SectionPageProtection,
4656                                       AllocationAttributes,
4657                                       FileObject);
4658     }
4659 #endif
4660     else
4661     {
4662         /* All cases should be handled above */
4663         Status = STATUS_INVALID_PARAMETER;
4664     }
4665 
4666     if (FileLock)
4667         FsRtlReleaseFile(FileObject);
4668     if (FileObject)
4669         ObDereferenceObject(FileObject);
4670 
4671     return Status;
4672 }
4673 
4674 BOOLEAN
4675 NTAPI
4676 MmArePagesResident(
4677     _In_ PEPROCESS Process,
4678     _In_ PVOID Address,
4679     _In_ ULONG Length)
4680 {
4681     PMEMORY_AREA MemoryArea;
4682     BOOLEAN Ret = TRUE;
4683     PMM_SECTION_SEGMENT Segment;
4684     LARGE_INTEGER SegmentOffset, RangeEnd;
4685     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
4686 
4687     MmLockAddressSpace(AddressSpace);
4688 
4689     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
4690     if (MemoryArea == NULL)
4691     {
4692         MmUnlockAddressSpace(AddressSpace);
4693         return FALSE;
4694     }
4695 
4696     /* Only supported in old Mm for now */
4697     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
4698     /* For file mappings */
4699     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
4700 
4701     Segment = MemoryArea->SectionData.Segment;
4702     MmLockSectionSegment(Segment);
4703 
4704     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
4705             + MemoryArea->SectionData.ViewOffset;
4706     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
4707             + MemoryArea->SectionData.ViewOffset;
4708 
4709     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4710     {
4711         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
4712         if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4713         {
4714             Ret = FALSE;
4715             break;
4716         }
4717         SegmentOffset.QuadPart += PAGE_SIZE;
4718     }
4719 
4720     MmUnlockSectionSegment(Segment);
4721 
4722     MmUnlockAddressSpace(AddressSpace);
4723     return Ret;
4724 }
4725 
4726 /* Like CcPurgeCache but for the in-memory segment */
4727 BOOLEAN
4728 NTAPI
4729 MmPurgeSegment(
4730     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4731     _In_opt_ PLARGE_INTEGER Offset,
4732     _In_ ULONG Length)
4733 {
4734     LARGE_INTEGER PurgeStart, PurgeEnd;
4735     PMM_SECTION_SEGMENT Segment;
4736 
4737     Segment = MiGrabDataSection(SectionObjectPointer);
4738     if (!Segment)
4739     {
4740         /* Nothing to purge */
4741         return TRUE;
4742     }
4743 
4744     PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4745     if (Length && Offset)
4746     {
4747         if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4748             return FALSE;
4749     }
4750 
4751     MmLockSectionSegment(Segment);
4752 
4753     if (!Length || !Offset)
4754     {
4755         /* We must calculate the length for ourselves */
4756         /* FIXME: All of this is suboptimal */
4757         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4758         /* No page. Nothing to purge */
4759         if (!ElemCount)
4760         {
4761             MmUnlockSectionSegment(Segment);
4762             MmDereferenceSegment(Segment);
4763             return TRUE;
4764         }
4765 
4766         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4767         PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4768     }
4769 
4770     while (PurgeStart.QuadPart < PurgeEnd.QuadPart)
4771     {
4772         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &PurgeStart);
4773 
4774         if (Entry == 0)
4775         {
4776             PurgeStart.QuadPart += PAGE_SIZE;
4777             continue;
4778         }
4779 
4780         if (IS_SWAP_FROM_SSE(Entry))
4781         {
4782             ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY);
4783             /* The page is currently being read. Meaning someone will need it soon. Bad luck */
4784             MmUnlockSectionSegment(Segment);
4785             MmDereferenceSegment(Segment);
4786             return FALSE;
4787         }
4788 
4789         if (IS_WRITE_SSE(Entry))
4790         {
4791             /* We're trying to purge an entry which is being written. Restart this loop iteration */
4792             MmUnlockSectionSegment(Segment);
4793             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4794             MmLockSectionSegment(Segment);
4795             continue;
4796         }
4797 
4798         if (SHARE_COUNT_FROM_SSE(Entry) > 0)
4799         {
4800             /* This page is currently in use. Bad luck */
4801             MmUnlockSectionSegment(Segment);
4802             MmDereferenceSegment(Segment);
4803             return FALSE;
4804         }
4805 
4806         /* We can let this page go */
4807         MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0);
4808         MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4809 
4810         PurgeStart.QuadPart += PAGE_SIZE;
4811     }
4812 
4813     /* This page is currently in use. Bad luck */
4814     MmUnlockSectionSegment(Segment);
4815     MmDereferenceSegment(Segment);
4816     return TRUE;
4817 }
4818 
4819 NTSTATUS
4820 NTAPI
4821 MmMakeDataSectionResident(
4822     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4823     _In_ LONGLONG Offset,
4824     _In_ ULONG Length,
4825     _In_ PLARGE_INTEGER ValidDataLength)
4826 {
4827     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4828 
4829     /* There must be a segment for this call */
4830     ASSERT(Segment);
4831 
4832     NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength, FALSE);
4833 
4834     MmDereferenceSegment(Segment);
4835 
4836     return Status;
4837 }
4838 
4839 NTSTATUS
4840 NTAPI
4841 MmFlushSegment(
4842     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4843     _In_opt_ PLARGE_INTEGER Offset,
4844     _In_ ULONG Length,
4845     _Out_opt_ PIO_STATUS_BLOCK Iosb)
4846 {
4847     LARGE_INTEGER FlushStart, FlushEnd;
4848     NTSTATUS Status;
4849 
4850     if (Offset)
4851     {
4852         FlushStart = *Offset;
4853         Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart);
4854         if (!NT_SUCCESS(Status))
4855             return Status;
4856     }
4857 
4858     if (Iosb)
4859         Iosb->Information = 0;
4860 
4861     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4862     if (!Segment)
4863     {
4864         /* Nothing to flush */
4865         if (Iosb)
4866             Iosb->Status = STATUS_SUCCESS;
4867         return STATUS_SUCCESS;
4868     }
4869 
4870     ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT);
4871 
4872     MmLockSectionSegment(Segment);
4873 
4874     if (!Offset)
4875     {
4876         FlushStart.QuadPart = 0;
4877 
4878         /* FIXME: All of this is suboptimal */
4879         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4880         /* No page. Nothing to flush */
4881         if (!ElemCount)
4882         {
4883             MmUnlockSectionSegment(Segment);
4884             MmDereferenceSegment(Segment);
4885             if (Iosb)
4886             {
4887                 Iosb->Status = STATUS_SUCCESS;
4888                 Iosb->Information = 0;
4889             }
4890             return STATUS_SUCCESS;
4891         }
4892 
4893         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4894         FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4895     }
4896 
4897     FlushStart.QuadPart >>= PAGE_SHIFT;
4898     FlushStart.QuadPart <<= PAGE_SHIFT;
4899 
4900     while (FlushStart.QuadPart < FlushEnd.QuadPart)
4901     {
4902         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &FlushStart);
4903 
4904         if (IS_DIRTY_SSE(Entry))
4905         {
4906             MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE);
4907 
4908             if (Iosb)
4909                 Iosb->Information += PAGE_SIZE;
4910         }
4911 
4912         FlushStart.QuadPart += PAGE_SIZE;
4913     }
4914 
4915     MmUnlockSectionSegment(Segment);
4916     MmDereferenceSegment(Segment);
4917 
4918     if (Iosb)
4919         Iosb->Status = STATUS_SUCCESS;
4920 
4921     return STATUS_SUCCESS;
4922 }
4923 
4924 _Requires_exclusive_lock_held_(Segment->Lock)
4925 BOOLEAN
4926 NTAPI
4927 MmCheckDirtySegment(
4928     PMM_SECTION_SEGMENT Segment,
4929     PLARGE_INTEGER Offset,
4930     BOOLEAN ForceDirty,
4931     BOOLEAN PageOut)
4932 {
4933     ULONG_PTR Entry;
4934     NTSTATUS Status;
4935     PFN_NUMBER Page;
4936 
4937     ASSERT(Segment->Locked);
4938 
4939     ASSERT((Offset->QuadPart % PAGE_SIZE) == 0);
4940 
4941     DPRINT("Checking segment for file %wZ at offset 0x%I64X.\n", &Segment->FileObject->FileName, Offset->QuadPart);
4942 
4943     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
4944     if (Entry == 0)
4945         return FALSE;
4946 
4947     Page = PFN_FROM_SSE(Entry);
4948     if ((IS_DIRTY_SSE(Entry)) || ForceDirty)
4949     {
4950         BOOLEAN DirtyAgain;
4951 
4952         /*
4953          * We got a dirty entry. This path is for the shared data,
4954          * be-it regular file maps or shared sections of DLLs
4955          */
4956         ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) ||
4957                FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
4958 
4959         /* Insert the cleaned entry back. Mark it as write in progress, and clear the dirty bit. */
4960         Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
4961         Entry = WRITE_SSE(Entry);
4962         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
4963 
4964         MmUnlockSectionSegment(Segment);
4965 
4966         if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
4967         {
4968             PERESOURCE ResourceToRelease = NULL;
4969             KIRQL OldIrql;
4970 
4971             /* We have to write it back to the file. Tell the FS driver who we are */
4972             if (PageOut)
4973             {
4974                 LARGE_INTEGER EndOffset = *Offset;
4975 
4976                 ASSERT(IoGetTopLevelIrp() == NULL);
4977 
4978                 /* We need to disable all APCs */
4979                 KeRaiseIrql(APC_LEVEL, &OldIrql);
4980 
4981                 EndOffset.QuadPart += PAGE_SIZE;
4982                 Status = FsRtlAcquireFileForModWriteEx(Segment->FileObject,
4983                                                        &EndOffset,
4984                                                        &ResourceToRelease);
4985                 if (NT_SUCCESS(Status))
4986                 {
4987                     IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
4988                 }
4989                 else
4990                 {
4991                     /* Make sure we will not try to release anything */
4992                     ResourceToRelease = NULL;
4993                 }
4994             }
4995             else
4996             {
4997                 /* We don't have to lock. Say this is success */
4998                 Status = STATUS_SUCCESS;
4999             }
5000 
5001             /* Go ahead and write the page, if previous locking succeeded */
5002             if (NT_SUCCESS(Status))
5003             {
5004                 DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
5005                         Offset->QuadPart, &Segment->FileObject->FileName, PageOut ? "TRUE" : "FALSE");
5006                 Status = MiWritePage(Segment, Offset->QuadPart, Page);
5007             }
5008 
5009             if (PageOut)
5010             {
5011                 IoSetTopLevelIrp(NULL);
5012                 if (ResourceToRelease != NULL)
5013                 {
5014                     FsRtlReleaseFileForModWrite(Segment->FileObject, ResourceToRelease);
5015                 }
5016                 KeLowerIrql(OldIrql);
5017             }
5018         }
5019         else
5020         {
5021             /* This must only be called by the page-out path */
5022             ASSERT(PageOut);
5023 
5024             /* And this must be for a shared section in a DLL */
5025             ASSERT(FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5026 
5027             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5028             if (!SwapEntry)
5029             {
5030                 SwapEntry = MmAllocSwapPage();
5031             }
5032 
5033             if (SwapEntry)
5034             {
5035                 Status = MmWriteToSwapPage(SwapEntry, Page);
5036                 if (NT_SUCCESS(Status))
5037                 {
5038                     MmSetSavedSwapEntryPage(Page, SwapEntry);
5039                 }
5040                 else
5041                 {
5042                     MmFreeSwapPage(SwapEntry);
5043                 }
5044             }
5045             else
5046             {
5047                 DPRINT1("Failed to allocate a swap page!\n");
5048                 Status = STATUS_INSUFFICIENT_RESOURCES;
5049             }
5050         }
5051 
5052         MmLockSectionSegment(Segment);
5053 
5054         /* Get the entry again */
5055         Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5056         ASSERT(PFN_FROM_SSE(Entry) == Page);
5057 
5058         if (!NT_SUCCESS(Status))
5059         {
5060             /* Damn, this failed. Consider this page as still dirty */
5061             DPRINT1("MiWritePage FAILED: Status 0x%08x!\n", Status);
5062             DirtyAgain = TRUE;
5063         }
5064         else
5065         {
5066             /* Check if someone dirtified this page while we were not looking */
5067             DirtyAgain = IS_DIRTY_SSE(Entry);
5068         }
5069 
5070         /* Drop the reference we got, deleting the write altogether. */
5071         Entry = MAKE_SSE(Page << PAGE_SHIFT, SHARE_COUNT_FROM_SSE(Entry) - 1);
5072         if (DirtyAgain)
5073         {
5074             Entry = DIRTY_SSE(Entry);
5075         }
5076         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5077     }
5078 
5079     /* Were this page hanging there just for the sake of being present ? */
5080     if (!IS_DIRTY_SSE(Entry) && (SHARE_COUNT_FROM_SSE(Entry) == 0) && PageOut)
5081     {
5082         ULONG_PTR NewEntry = 0;
5083         /* Restore the swap entry here */
5084         if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5085         {
5086             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5087             if (SwapEntry)
5088                 NewEntry = MAKE_SWAP_SSE(SwapEntry);
5089         }
5090 
5091         /* Yes. Release it */
5092         MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
5093         MmReleasePageMemoryConsumer(MC_USER, Page);
5094         /* Tell the caller we released the page */
5095         return TRUE;
5096     }
5097 
5098     return FALSE;
5099 }
5100 
5101 NTSTATUS
5102 NTAPI
5103 MmMakePagesDirty(
5104     _In_ PEPROCESS Process,
5105     _In_ PVOID Address,
5106     _In_ ULONG Length)
5107 {
5108     PMEMORY_AREA MemoryArea;
5109     PMM_SECTION_SEGMENT Segment;
5110     LARGE_INTEGER SegmentOffset, RangeEnd;
5111     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
5112 
5113     MmLockAddressSpace(AddressSpace);
5114 
5115     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
5116     if (MemoryArea == NULL)
5117     {
5118         DPRINT1("Unable to find memory area at address %p.\n", Address);
5119         MmUnlockAddressSpace(AddressSpace);
5120         return STATUS_NOT_MAPPED_VIEW;
5121     }
5122 
5123     /* Only supported in old Mm for now */
5124     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
5125     /* For file mappings */
5126     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
5127 
5128     Segment = MemoryArea->SectionData.Segment;
5129     MmLockSectionSegment(Segment);
5130 
5131     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
5132             + MemoryArea->SectionData.ViewOffset;
5133     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
5134             + MemoryArea->SectionData.ViewOffset;
5135 
5136     DPRINT("MmMakePagesResident: Segment %p, 0x%I64x -> 0x%I64x\n", Segment, SegmentOffset.QuadPart, RangeEnd.QuadPart);
5137 
5138     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
5139     {
5140         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5141 
5142         /* Let any pending read proceed */
5143         while (MM_IS_WAIT_PTE(Entry))
5144         {
5145             MmUnlockSectionSegment(Segment);
5146             MmUnlockAddressSpace(AddressSpace);
5147             YieldProcessor();
5148             MmLockAddressSpace(AddressSpace);
5149             MmLockSectionSegment(Segment);
5150             Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5151         }
5152 
5153         /* We are called from Cc, this can't be backed by the page files */
5154         ASSERT(!IS_SWAP_FROM_SSE(Entry));
5155 
5156         /* If there is no page there, there is nothing to make dirty */
5157         if (Entry != 0)
5158         {
5159             /* Dirtify the entry */
5160             MmSetPageEntrySectionSegment(Segment, &SegmentOffset, DIRTY_SSE(Entry));
5161         }
5162 
5163         SegmentOffset.QuadPart += PAGE_SIZE;
5164     }
5165 
5166     MmUnlockSectionSegment(Segment);
5167 
5168     MmUnlockAddressSpace(AddressSpace);
5169     return STATUS_SUCCESS;
5170 }
5171 
5172 NTSTATUS
5173 NTAPI
5174 MmExtendSection(
5175     _In_ PVOID _Section,
5176     _Inout_ PLARGE_INTEGER NewSize)
5177 {
5178     PSECTION Section = _Section;
5179 
5180     /* It makes no sense to extend an image mapping */
5181     if (Section->u.Flags.Image)
5182         return STATUS_SECTION_NOT_EXTENDED;
5183 
5184     /* Nor is it possible to extend a page file mapping */
5185     if (!Section->u.Flags.File)
5186         return STATUS_SECTION_NOT_EXTENDED;
5187 
5188     if (!MiIsRosSectionObject(Section))
5189         return STATUS_NOT_IMPLEMENTED;
5190 
5191     /* We just extend the sizes. Shrinking is a no-op ? */
5192     if (NewSize->QuadPart > Section->SizeOfSection.QuadPart)
5193     {
5194         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
5195         Section->SizeOfSection = *NewSize;
5196 
5197         if (!Section->u.Flags.Reserve)
5198         {
5199             MmLockSectionSegment(Segment);
5200             if (Segment->RawLength.QuadPart < NewSize->QuadPart)
5201             {
5202                 Segment->RawLength = *NewSize;
5203                 Segment->Length.QuadPart = (NewSize->QuadPart + PAGE_SIZE - 1) & ~((LONGLONG)PAGE_SIZE);
5204             }
5205             MmUnlockSectionSegment(Segment);
5206         }
5207     }
5208 
5209     return STATUS_SUCCESS;
5210 }
5211 
5212 /* EOF */
5213