xref: /reactos/ntoskrnl/mm/section.c (revision ccef43f3)
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->Vm;
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         PMM_SECTION_SEGMENT Segment = MemoryArea->SectionData.Segment;
3649         PMMVAD Vad = &MemoryArea->VadNode;
3650         PFILE_OBJECT FileObject;
3651         SIZE_T ViewSize;
3652         LARGE_INTEGER ViewOffset;
3653         ViewOffset.QuadPart = MemoryArea->SectionData.ViewOffset;
3654 
3655         InterlockedIncrement64(Segment->ReferenceCount);
3656 
3657         Status = MmUnmapViewOfSegment(AddressSpace, BaseAddress);
3658         if (!NT_SUCCESS(Status))
3659         {
3660             DPRINT1("MmUnmapViewOfSegment failed for %p (Process %p) with %lx\n",
3661                     BaseAddress, Process, Status);
3662             ASSERT(NT_SUCCESS(Status));
3663         }
3664 
3665         if (FlagOn(*Segment->Flags, MM_PHYSICALMEMORY_SEGMENT))
3666         {
3667             /* Don't bother */
3668             MmDereferenceSegment(Segment);
3669             return STATUS_SUCCESS;
3670         }
3671         ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT));
3672 
3673         FileObject = Segment->FileObject;
3674         FsRtlAcquireFileExclusive(FileObject);
3675 
3676         /* Don't bother for auto-delete closed file. */
3677         if (FlagOn(FileObject->Flags, FO_DELETE_ON_CLOSE) && FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE))
3678         {
3679             FsRtlReleaseFile(FileObject);
3680             MmDereferenceSegment(Segment);
3681             return STATUS_SUCCESS;
3682         }
3683 
3684         /*
3685          * Flush only when last mapping is deleted.
3686          * FIXME: Why Vad->ControlArea == NULL?
3687          */
3688         if (Vad->ControlArea == NULL || Vad->ControlArea->NumberOfMappedViews == 1)
3689         {
3690             ViewSize = PAGE_SIZE + ((Vad->EndingVpn - Vad->StartingVpn) << PAGE_SHIFT);
3691             while (ViewSize > 0)
3692             {
3693                 ULONG FlushSize = min(ViewSize, PAGE_ROUND_DOWN(MAXULONG));
3694                 MmFlushSegment(FileObject->SectionObjectPointer,
3695                                &ViewOffset,
3696                                FlushSize,
3697                                NULL);
3698                 ViewSize -= FlushSize;
3699                 ViewOffset.QuadPart += FlushSize;
3700             }
3701         }
3702 
3703         FsRtlReleaseFile(FileObject);
3704         MmDereferenceSegment(Segment);
3705     }
3706 
3707     /* Notify debugger */
3708     if (ImageBaseAddress && !SkipDebuggerNotify) DbgkUnMapViewOfSection(ImageBaseAddress);
3709 
3710     return STATUS_SUCCESS;
3711 }
3712 
3713 
3714 
3715 
3716 /**
3717  * Queries the information of a section object.
3718  *
3719  * @param SectionHandle
3720  *        Handle to the section object. It must be opened with SECTION_QUERY
3721  *        access.
3722  * @param SectionInformationClass
3723  *        Index to a certain information structure. Can be either
3724  *        SectionBasicInformation or SectionImageInformation. The latter
3725  *        is valid only for sections that were created with the SEC_IMAGE
3726  *        flag.
3727  * @param SectionInformation
3728  *        Caller supplies storage for resulting information.
3729  * @param Length
3730  *        Size of the supplied storage.
3731  * @param ResultLength
3732  *        Data written.
3733  *
3734  * @return Status.
3735  *
3736  * @implemented
3737  */
3738 NTSTATUS
3739 NTAPI
3740 NtQuerySection(
3741     _In_ HANDLE SectionHandle,
3742     _In_ SECTION_INFORMATION_CLASS SectionInformationClass,
3743     _Out_ PVOID SectionInformation,
3744     _In_ SIZE_T SectionInformationLength,
3745     _Out_opt_ PSIZE_T ResultLength)
3746 {
3747     PSECTION Section;
3748     KPROCESSOR_MODE PreviousMode;
3749     NTSTATUS Status;
3750     PAGED_CODE();
3751 
3752     PreviousMode = ExGetPreviousMode();
3753     if (PreviousMode != KernelMode)
3754     {
3755         _SEH2_TRY
3756         {
3757             ProbeForWrite(SectionInformation,
3758                           SectionInformationLength,
3759                           __alignof(ULONG));
3760             if (ResultLength != NULL)
3761             {
3762                 ProbeForWrite(ResultLength,
3763                               sizeof(*ResultLength),
3764                               __alignof(SIZE_T));
3765             }
3766         }
3767         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3768         {
3769             _SEH2_YIELD(return _SEH2_GetExceptionCode());
3770         }
3771         _SEH2_END;
3772     }
3773 
3774     if (SectionInformationClass == SectionBasicInformation)
3775     {
3776         if (SectionInformationLength < sizeof(SECTION_BASIC_INFORMATION))
3777         {
3778             return STATUS_INFO_LENGTH_MISMATCH;
3779         }
3780     }
3781     else if (SectionInformationClass == SectionImageInformation)
3782     {
3783         if (SectionInformationLength < sizeof(SECTION_IMAGE_INFORMATION))
3784         {
3785             return STATUS_INFO_LENGTH_MISMATCH;
3786         }
3787     }
3788     else
3789     {
3790         return STATUS_INVALID_INFO_CLASS;
3791     }
3792 
3793     Status = ObReferenceObjectByHandle(SectionHandle,
3794                                        SECTION_QUERY,
3795                                        MmSectionObjectType,
3796                                        PreviousMode,
3797                                        (PVOID*)(PVOID)&Section,
3798                                        NULL);
3799     if (!NT_SUCCESS(Status))
3800     {
3801         DPRINT1("Failed to reference section: 0x%lx\n", Status);
3802         return Status;
3803     }
3804 
3805     switch(SectionInformationClass)
3806     {
3807         case SectionBasicInformation:
3808         {
3809             SECTION_BASIC_INFORMATION Sbi;
3810 
3811             Sbi.Size = Section->SizeOfSection;
3812             Sbi.BaseAddress = (PVOID)Section->Address.StartingVpn;
3813 
3814             Sbi.Attributes = 0;
3815             if (Section->u.Flags.File)
3816                 Sbi.Attributes |= SEC_FILE;
3817             if (Section->u.Flags.Image)
3818                 Sbi.Attributes |= SEC_IMAGE;
3819 
3820             /* Those are not set *************
3821             if (Section->u.Flags.Commit)
3822                 Sbi.Attributes |= SEC_COMMIT;
3823             if (Section->u.Flags.Reserve)
3824                 Sbi.Attributes |= SEC_RESERVE;
3825             **********************************/
3826 
3827             if (Section->u.Flags.Image)
3828             {
3829                 if (MiIsRosSectionObject(Section))
3830                 {
3831                     PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3832                     Sbi.BaseAddress = 0;
3833                     Sbi.Size.QuadPart = ImageSectionObject->ImageInformation.ImageFileSize;
3834                 }
3835                 else
3836                 {
3837                     /* Not supported yet */
3838                     ASSERT(FALSE);
3839                 }
3840             }
3841             else if (MiIsRosSectionObject(Section))
3842             {
3843                 Sbi.BaseAddress = (PVOID)((PMM_SECTION_SEGMENT)Section->Segment)->Image.VirtualAddress;
3844                 Sbi.Size.QuadPart = ((PMM_SECTION_SEGMENT)Section->Segment)->RawLength.QuadPart;
3845             }
3846             else
3847             {
3848                 DPRINT1("Unimplemented code path!");
3849             }
3850 
3851             _SEH2_TRY
3852             {
3853                 *((SECTION_BASIC_INFORMATION*)SectionInformation) = Sbi;
3854                 if (ResultLength != NULL)
3855                 {
3856                     *ResultLength = sizeof(Sbi);
3857                 }
3858             }
3859             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3860             {
3861                 Status = _SEH2_GetExceptionCode();
3862             }
3863             _SEH2_END;
3864             break;
3865         }
3866         case SectionImageInformation:
3867         {
3868             if (!Section->u.Flags.Image)
3869             {
3870                 Status = STATUS_SECTION_NOT_IMAGE;
3871             }
3872             else if (MiIsRosSectionObject(Section))
3873             {
3874                 PMM_IMAGE_SECTION_OBJECT ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
3875 
3876                 _SEH2_TRY
3877                 {
3878                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3879                     *Sii = ImageSectionObject->ImageInformation;
3880                     if (ResultLength != NULL)
3881                     {
3882                         *ResultLength = sizeof(*Sii);
3883                     }
3884                 }
3885                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3886                 {
3887                     Status = _SEH2_GetExceptionCode();
3888                 }
3889                 _SEH2_END;
3890             }
3891             else
3892             {
3893                 _SEH2_TRY
3894                 {
3895                     PSECTION_IMAGE_INFORMATION Sii = (PSECTION_IMAGE_INFORMATION)SectionInformation;
3896                     *Sii = *Section->Segment->u2.ImageInformation;
3897                     if (ResultLength != NULL)
3898                         *ResultLength = sizeof(*Sii);
3899                 }
3900                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3901                 {
3902                     Status = _SEH2_GetExceptionCode();
3903                 }
3904                 _SEH2_END;
3905             }
3906             break;
3907         }
3908         default:
3909             DPRINT1("Unknown SectionInformationClass: %d\n", SectionInformationClass);
3910             Status = STATUS_NOT_SUPPORTED;
3911     }
3912 
3913     ObDereferenceObject(Section);
3914 
3915     return Status;
3916 }
3917 
3918 /**********************************************************************
3919  * NAME       EXPORTED
3920  * MmMapViewOfSection
3921  *
3922  * DESCRIPTION
3923  * Maps a view of a section into the virtual address space of a
3924  * process.
3925  *
3926  * ARGUMENTS
3927  * Section
3928  *  Pointer to the section object.
3929  *
3930  * ProcessHandle
3931  *  Pointer to the process.
3932  *
3933  * BaseAddress
3934  *  Desired base address (or NULL) on entry;
3935  *  Actual base address of the view on exit.
3936  *
3937  * ZeroBits
3938  *  Number of high order address bits that must be zero.
3939  *
3940  * CommitSize
3941  *  Size in bytes of the initially committed section of
3942  *  the view.
3943  *
3944  * SectionOffset
3945  *  Offset in bytes from the beginning of the section
3946  *  to the beginning of the view.
3947  *
3948  * ViewSize
3949  *  Desired length of map (or zero to map all) on entry
3950  *  Actual length mapped on exit.
3951  *
3952  * InheritDisposition
3953  *  Specified how the view is to be shared with
3954  *  child processes.
3955  *
3956  * AllocationType
3957  *  Type of allocation for the pages.
3958  *
3959  * Protect
3960  *  Protection for the committed region of the view.
3961  *
3962  * RETURN VALUE
3963  * Status.
3964  *
3965  * @implemented
3966  */
3967 NTSTATUS NTAPI
3968 MmMapViewOfSection(IN PVOID SectionObject,
3969                    IN PEPROCESS Process,
3970                    IN OUT PVOID *BaseAddress,
3971                    IN ULONG_PTR ZeroBits,
3972                    IN SIZE_T CommitSize,
3973                    IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
3974                    IN OUT PSIZE_T ViewSize,
3975                    IN SECTION_INHERIT InheritDisposition,
3976                    IN ULONG AllocationType,
3977                    IN ULONG Protect)
3978 {
3979     PSECTION Section;
3980     PMMSUPPORT AddressSpace;
3981     NTSTATUS Status = STATUS_SUCCESS;
3982     BOOLEAN NotAtBase = FALSE;
3983 
3984     if (MiIsRosSectionObject(SectionObject) == FALSE)
3985     {
3986         DPRINT("Mapping ARM3 section into %s\n", Process->ImageFileName);
3987         return MmMapViewOfArm3Section(SectionObject,
3988                                       Process,
3989                                       BaseAddress,
3990                                       ZeroBits,
3991                                       CommitSize,
3992                                       SectionOffset,
3993                                       ViewSize,
3994                                       InheritDisposition,
3995                                       AllocationType,
3996                                       Protect);
3997     }
3998 
3999     ASSERT(Process);
4000 
4001     if (!Protect || Protect & ~PAGE_FLAGS_VALID_FOR_SECTION)
4002     {
4003         return STATUS_INVALID_PAGE_PROTECTION;
4004     }
4005 
4006     /* FIXME: We should keep this, but it would break code checking equality */
4007     Protect &= ~PAGE_NOCACHE;
4008 
4009     Section = SectionObject;
4010     AddressSpace = &Process->Vm;
4011 
4012     if (Section->u.Flags.NoChange)
4013         AllocationType |= SEC_NO_CHANGE;
4014 
4015     MmLockAddressSpace(AddressSpace);
4016 
4017     if (Section->u.Flags.Image)
4018     {
4019         ULONG i;
4020         ULONG NrSegments;
4021         ULONG_PTR ImageBase;
4022         SIZE_T ImageSize;
4023         PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
4024         PMM_SECTION_SEGMENT SectionSegments;
4025 
4026         ImageSectionObject = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment);
4027         SectionSegments = ImageSectionObject->Segments;
4028         NrSegments = ImageSectionObject->NrSegments;
4029 
4030         ASSERT(ImageSectionObject->RefCount > 0);
4031 
4032         ImageBase = (ULONG_PTR)*BaseAddress;
4033         if (ImageBase == 0)
4034         {
4035             ImageBase = (ULONG_PTR)ImageSectionObject->BasedAddress;
4036         }
4037 
4038         ImageSize = 0;
4039         for (i = 0; i < NrSegments; i++)
4040         {
4041             ULONG_PTR MaxExtent;
4042             MaxExtent = (ULONG_PTR)(SectionSegments[i].Image.VirtualAddress +
4043                                     SectionSegments[i].Length.QuadPart);
4044             ImageSize = max(ImageSize, MaxExtent);
4045         }
4046 
4047         ImageSectionObject->ImageInformation.ImageFileSize = (ULONG)ImageSize;
4048 
4049         /* Check for an illegal base address */
4050         if (((ImageBase + ImageSize) > (ULONG_PTR)MM_HIGHEST_VAD_ADDRESS) ||
4051                 ((ImageBase + ImageSize) < ImageSize))
4052         {
4053             ASSERT(*BaseAddress == NULL);
4054             ImageBase = ALIGN_DOWN_BY((ULONG_PTR)MM_HIGHEST_VAD_ADDRESS - ImageSize,
4055                                       MM_VIRTMEM_GRANULARITY);
4056             NotAtBase = TRUE;
4057         }
4058         else if (ImageBase != ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY))
4059         {
4060             ASSERT(*BaseAddress == NULL);
4061             ImageBase = ALIGN_DOWN_BY(ImageBase, MM_VIRTMEM_GRANULARITY);
4062             NotAtBase = TRUE;
4063         }
4064 
4065         /* Check there is enough space to map the section at that point. */
4066         if (MmLocateMemoryAreaByRegion(AddressSpace, (PVOID)ImageBase,
4067                                        PAGE_ROUND_UP(ImageSize)) != NULL)
4068         {
4069             /* Fail if the user requested a fixed base address. */
4070             if ((*BaseAddress) != NULL)
4071             {
4072                 MmUnlockAddressSpace(AddressSpace);
4073                 return STATUS_CONFLICTING_ADDRESSES;
4074             }
4075             /* Otherwise find a gap to map the image. */
4076             ImageBase = (ULONG_PTR)MmFindGap(AddressSpace, PAGE_ROUND_UP(ImageSize), MM_VIRTMEM_GRANULARITY, FALSE);
4077             if (ImageBase == 0)
4078             {
4079                 MmUnlockAddressSpace(AddressSpace);
4080                 return STATUS_CONFLICTING_ADDRESSES;
4081             }
4082             /* Remember that we loaded image at a different base address */
4083             NotAtBase = TRUE;
4084         }
4085 
4086         for (i = 0; i < NrSegments; i++)
4087         {
4088             PVOID SBaseAddress = (PVOID)
4089                                  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4090             MmLockSectionSegment(&SectionSegments[i]);
4091             Status = MmMapViewOfSegment(AddressSpace,
4092                                         TRUE,
4093                                         &SectionSegments[i],
4094                                         &SBaseAddress,
4095                                         SectionSegments[i].Length.QuadPart,
4096                                         SectionSegments[i].Protection,
4097                                         0,
4098                                         0);
4099             MmUnlockSectionSegment(&SectionSegments[i]);
4100             if (!NT_SUCCESS(Status))
4101             {
4102                 /* roll-back */
4103                 while (i--)
4104                 {
4105                     SBaseAddress =  ((char*)ImageBase + (ULONG_PTR)SectionSegments[i].Image.VirtualAddress);
4106                     MmLockSectionSegment(&SectionSegments[i]);
4107                     MmUnmapViewOfSegment(AddressSpace, SBaseAddress);
4108                     MmUnlockSectionSegment(&SectionSegments[i]);
4109                 }
4110 
4111                 MmUnlockAddressSpace(AddressSpace);
4112                 return Status;
4113             }
4114         }
4115 
4116         *BaseAddress = (PVOID)ImageBase;
4117         *ViewSize = ImageSize;
4118 
4119         DPRINT("Mapped %p for section pointer %p\n", ImageSectionObject, ImageSectionObject->FileObject->SectionObjectPointer);
4120 
4121         /* One more map */
4122         InterlockedIncrement(&ImageSectionObject->MapCount);
4123     }
4124     else
4125     {
4126         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4127         LONGLONG ViewOffset;
4128 
4129         ASSERT(Segment->RefCount > 0);
4130 
4131         /* check for write access */
4132         if ((Protect & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)) &&
4133                 !(Section->InitialPageProtection & (PAGE_READWRITE|PAGE_EXECUTE_READWRITE)))
4134         {
4135             MmUnlockAddressSpace(AddressSpace);
4136             return STATUS_SECTION_PROTECTION;
4137         }
4138         /* check for read access */
4139         if ((Protect & (PAGE_READONLY|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_WRITECOPY)) &&
4140                 !(Section->InitialPageProtection & (PAGE_READONLY|PAGE_READWRITE|PAGE_WRITECOPY|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4141         {
4142             MmUnlockAddressSpace(AddressSpace);
4143             return STATUS_SECTION_PROTECTION;
4144         }
4145         /* check for execute access */
4146         if ((Protect & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)) &&
4147                 !(Section->InitialPageProtection & (PAGE_EXECUTE|PAGE_EXECUTE_READ|PAGE_EXECUTE_READWRITE|PAGE_EXECUTE_WRITECOPY)))
4148         {
4149             MmUnlockAddressSpace(AddressSpace);
4150             return STATUS_SECTION_PROTECTION;
4151         }
4152 
4153         if (SectionOffset == NULL)
4154         {
4155             ViewOffset = 0;
4156         }
4157         else
4158         {
4159             ViewOffset = SectionOffset->QuadPart;
4160         }
4161 
4162         if ((ViewOffset % PAGE_SIZE) != 0)
4163         {
4164             MmUnlockAddressSpace(AddressSpace);
4165             return STATUS_MAPPED_ALIGNMENT;
4166         }
4167 
4168         if ((*ViewSize) == 0)
4169         {
4170             (*ViewSize) = Section->SizeOfSection.QuadPart - ViewOffset;
4171         }
4172         else if ((ExGetPreviousMode() == UserMode) &&
4173             (((*ViewSize)+ViewOffset) > Section->SizeOfSection.QuadPart) &&
4174             (!Section->u.Flags.Reserve))
4175         {
4176             /* Dubious */
4177             (*ViewSize) = MIN(Section->SizeOfSection.QuadPart - ViewOffset, SIZE_T_MAX - PAGE_SIZE);
4178         }
4179 
4180         *ViewSize = PAGE_ROUND_UP(*ViewSize);
4181 
4182         MmLockSectionSegment(Segment);
4183         Status = MmMapViewOfSegment(AddressSpace,
4184                                     FALSE,
4185                                     Segment,
4186                                     BaseAddress,
4187                                     *ViewSize,
4188                                     Protect,
4189                                     ViewOffset,
4190                                     AllocationType & (MEM_TOP_DOWN|SEC_NO_CHANGE));
4191         MmUnlockSectionSegment(Segment);
4192         if (!NT_SUCCESS(Status))
4193         {
4194             MmUnlockAddressSpace(AddressSpace);
4195             return Status;
4196         }
4197     }
4198 
4199     MmUnlockAddressSpace(AddressSpace);
4200 
4201     if (NotAtBase)
4202         Status = STATUS_IMAGE_NOT_AT_BASE;
4203     else
4204         Status = STATUS_SUCCESS;
4205 
4206     return Status;
4207 }
4208 
4209 /*
4210  * @unimplemented
4211  */
4212 BOOLEAN NTAPI
4213 MmCanFileBeTruncated (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4214                       IN PLARGE_INTEGER   NewFileSize)
4215 {
4216     BOOLEAN Ret;
4217     PMM_SECTION_SEGMENT Segment;
4218 
4219     /* Check whether an ImageSectionObject exists */
4220     if (SectionObjectPointer->ImageSectionObject != NULL)
4221     {
4222         DPRINT1("ERROR: File can't be truncated because it has an image section\n");
4223         return FALSE;
4224     }
4225 
4226     Segment = MiGrabDataSection(SectionObjectPointer);
4227     if (!Segment)
4228     {
4229         /* There is no data section. It's fine to do anything. */
4230         return TRUE;
4231     }
4232 
4233     MmLockSectionSegment(Segment);
4234     if ((Segment->SectionCount == 0) ||
4235         ((Segment->SectionCount == 1) && (SectionObjectPointer->SharedCacheMap != NULL)))
4236     {
4237         /* If the cache is the only one holding a reference to the segment, then it's fine to resize */
4238         Ret = TRUE;
4239     }
4240     else
4241     {
4242         /* We can't shrink, but we can extend */
4243         Ret = NewFileSize->QuadPart >= Segment->RawLength.QuadPart;
4244 #if DBG
4245         if (!Ret)
4246         {
4247             DPRINT1("Cannot truncate data: New Size %I64d, Segment Size %I64d\n", NewFileSize->QuadPart, Segment->RawLength.QuadPart);
4248         }
4249 #endif
4250     }
4251     MmUnlockSectionSegment(Segment);
4252     MmDereferenceSegment(Segment);
4253 
4254     DPRINT("FIXME: didn't check for outstanding write probes\n");
4255 
4256     return Ret;
4257 }
4258 
4259 static
4260 BOOLEAN
4261 MiPurgeImageSegment(PMM_SECTION_SEGMENT Segment)
4262 {
4263     PCACHE_SECTION_PAGE_TABLE PageTable;
4264 
4265     MmLockSectionSegment(Segment);
4266 
4267     /* Loop over all entries */
4268     for (PageTable = RtlEnumerateGenericTable(&Segment->PageTable, TRUE);
4269          PageTable != NULL;
4270          PageTable = RtlEnumerateGenericTable(&Segment->PageTable, FALSE))
4271     {
4272         for (ULONG i = 0; i < _countof(PageTable->PageEntries); i++)
4273         {
4274             ULONG_PTR Entry = PageTable->PageEntries[i];
4275             LARGE_INTEGER Offset;
4276 
4277             if (!Entry)
4278                 continue;
4279 
4280             if (IS_SWAP_FROM_SSE(Entry) || (SHARE_COUNT_FROM_SSE(Entry) > 0))
4281             {
4282                 /* I/O ongoing or swap entry. Someone mapped this file as we were not looking */
4283                 MmUnlockSectionSegment(Segment);
4284                 return FALSE;
4285             }
4286 
4287             /* Regular entry */
4288             ASSERT(!IS_WRITE_SSE(Entry));
4289             ASSERT(MmGetSavedSwapEntryPage(PFN_FROM_SSE(Entry)) == 0);
4290 
4291             /* Properly remove using the used API */
4292             Offset.QuadPart = PageTable->FileOffset.QuadPart + (i << PAGE_SHIFT);
4293             MmSetPageEntrySectionSegment(Segment, &Offset, 0);
4294             MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4295         }
4296     }
4297 
4298     MmUnlockSectionSegment(Segment);
4299 
4300     return TRUE;
4301 }
4302 
4303 /*
4304  * @implemented
4305  */
4306 BOOLEAN NTAPI
4307 MmFlushImageSection (IN PSECTION_OBJECT_POINTERS SectionObjectPointer,
4308                      IN MMFLUSH_TYPE FlushType)
4309 {
4310     switch(FlushType)
4311     {
4312         case MmFlushForDelete:
4313         {
4314             /*
4315              * FIXME: Check for outstanding write probes on Data section.
4316              * How do we do that ?
4317              */
4318         }
4319         /* Fall-through */
4320         case MmFlushForWrite:
4321         {
4322             KIRQL OldIrql = MiAcquirePfnLock();
4323             PMM_IMAGE_SECTION_OBJECT ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4324 
4325             DPRINT("Deleting or modifying %p\n", SectionObjectPointer);
4326 
4327             /* Wait for concurrent creation or deletion of image to be done */
4328             ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4329             while (ImageSectionObject && (ImageSectionObject->SegFlags & (MM_SEGMENT_INCREATE | MM_SEGMENT_INDELETE)))
4330             {
4331                 MiReleasePfnLock(OldIrql);
4332                 KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4333                 OldIrql = MiAcquirePfnLock();
4334                 ImageSectionObject = SectionObjectPointer->ImageSectionObject;
4335             }
4336 
4337             if (!ImageSectionObject)
4338             {
4339                 DPRINT("No image section object. Accepting\n");
4340                 /* Nothing to do */
4341                 MiReleasePfnLock(OldIrql);
4342                 return TRUE;
4343             }
4344 
4345             /* Do we have open sections or mappings on it ? */
4346             if ((ImageSectionObject->SectionCount) || (ImageSectionObject->MapCount))
4347             {
4348                 /* We do. No way to delete it */
4349                 MiReleasePfnLock(OldIrql);
4350                 DPRINT("Denying. There are mappings open\n");
4351                 return FALSE;
4352             }
4353 
4354             /* There are no sections open on it, but we must still have pages around. Discard everything */
4355             ImageSectionObject->SegFlags |= MM_IMAGE_SECTION_FLUSH_DELETE;
4356             InterlockedIncrement64(&ImageSectionObject->RefCount);
4357             MiReleasePfnLock(OldIrql);
4358 
4359             DPRINT("Purging\n");
4360 
4361             for (ULONG i = 0; i < ImageSectionObject->NrSegments; i++)
4362             {
4363                 if (!MiPurgeImageSegment(&ImageSectionObject->Segments[i]))
4364                     break;
4365             }
4366 
4367             /* Grab lock again */
4368             OldIrql = MiAcquirePfnLock();
4369 
4370             if (!(ImageSectionObject->SegFlags & MM_IMAGE_SECTION_FLUSH_DELETE))
4371             {
4372                 /*
4373                  * Someone actually created a section while we were not looking.
4374                  * Drop our ref and deny.
4375                  * MmDereferenceSegmentWithLock releases Pfn lock
4376                  */
4377                 MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4378                 return FALSE;
4379             }
4380 
4381             /* We should be the last one holding a ref here. */
4382             ASSERT(ImageSectionObject->RefCount == 1);
4383             ASSERT(ImageSectionObject->SectionCount == 0);
4384 
4385             /* Dereference the first segment, this will free everything & release the lock */
4386             MmDereferenceSegmentWithLock(&ImageSectionObject->Segments[0], OldIrql);
4387             return TRUE;
4388         }
4389     }
4390     return FALSE;
4391 }
4392 
4393 /*
4394  * @implemented
4395  */
4396 NTSTATUS
4397 NTAPI
4398 MmMapViewInSystemSpace (IN PVOID SectionObject,
4399                         OUT PVOID * MappedBase,
4400                         IN OUT PSIZE_T ViewSize)
4401 {
4402     LARGE_INTEGER SectionOffset;
4403 
4404     SectionOffset.QuadPart = 0;
4405 
4406     return MmMapViewInSystemSpaceEx(SectionObject, MappedBase, ViewSize, &SectionOffset, 0);
4407 }
4408 
4409 NTSTATUS
4410 NTAPI
4411 MmMapViewInSystemSpaceEx (
4412     _In_ PVOID SectionObject,
4413     _Outptr_result_bytebuffer_ (*ViewSize) PVOID *MappedBase,
4414     _Inout_ PSIZE_T ViewSize,
4415     _Inout_ PLARGE_INTEGER SectionOffset,
4416     _In_ ULONG_PTR Flags
4417     )
4418 {
4419     PSECTION Section = SectionObject;
4420     PMM_SECTION_SEGMENT Segment;
4421     PMMSUPPORT AddressSpace;
4422     NTSTATUS Status;
4423 
4424     UNREFERENCED_PARAMETER(Flags);
4425 
4426     PAGED_CODE();
4427 
4428     if (MiIsRosSectionObject(SectionObject) == FALSE)
4429     {
4430         return MiMapViewInSystemSpace(SectionObject,
4431                                       &MmSession,
4432                                       MappedBase,
4433                                       ViewSize,
4434                                       SectionOffset);
4435     }
4436 
4437     DPRINT("MmMapViewInSystemSpaceEx() called\n");
4438 
4439     /* unsupported for now */
4440     ASSERT(Section->u.Flags.Image == 0);
4441 
4442     Section = SectionObject;
4443     Segment = (PMM_SECTION_SEGMENT)Section->Segment;
4444 
4445     if (*ViewSize == 0)
4446     {
4447         LONGLONG MapSizeLL;
4448 
4449         /* Page-align the mapping */
4450         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4451 
4452         if (!NT_SUCCESS(RtlLongLongSub(Section->SizeOfSection.QuadPart, SectionOffset->QuadPart, &MapSizeLL)))
4453             return STATUS_INVALID_VIEW_SIZE;
4454 
4455         if (!NT_SUCCESS(RtlLongLongToSIZET(MapSizeLL, ViewSize)))
4456             return STATUS_INVALID_VIEW_SIZE;
4457     }
4458     else
4459     {
4460         LONGLONG HelperLL;
4461 
4462         /* Get the map end */
4463         if (!NT_SUCCESS(RtlLongLongAdd(SectionOffset->QuadPart, *ViewSize, &HelperLL)))
4464             return STATUS_INVALID_VIEW_SIZE;
4465 
4466         /* Round it up, if needed */
4467         if (HelperLL % PAGE_SIZE)
4468         {
4469             if (!NT_SUCCESS(RtlLongLongAdd(HelperLL, PAGE_SIZE - (HelperLL % PAGE_SIZE), &HelperLL)))
4470                 return STATUS_INVALID_VIEW_SIZE;
4471         }
4472 
4473         /* Now that we have the mapping end, we can align down its start */
4474         SectionOffset->LowPart = PAGE_ROUND_DOWN(SectionOffset->LowPart);
4475 
4476         /* Get the new size */
4477         if (!NT_SUCCESS(RtlLongLongSub(HelperLL, SectionOffset->QuadPart, &HelperLL)))
4478             return STATUS_INVALID_VIEW_SIZE;
4479 
4480         if (!NT_SUCCESS(RtlLongLongToSIZET(HelperLL, ViewSize)))
4481             return STATUS_INVALID_VIEW_SIZE;
4482     }
4483 
4484     AddressSpace = MmGetKernelAddressSpace();
4485 
4486     MmLockAddressSpace(AddressSpace);
4487 
4488     MmLockSectionSegment(Segment);
4489 
4490     Status = MmMapViewOfSegment(AddressSpace,
4491                                 Section->u.Flags.Image,
4492                                 Segment,
4493                                 MappedBase,
4494                                 *ViewSize,
4495                                 PAGE_READWRITE,
4496                                 SectionOffset->QuadPart,
4497                                 SEC_RESERVE);
4498 
4499     MmUnlockSectionSegment(Segment);
4500     MmUnlockAddressSpace(AddressSpace);
4501 
4502     return Status;
4503 }
4504 
4505 /* This function must be called with adress space lock held */
4506 NTSTATUS
4507 NTAPI
4508 MiRosUnmapViewInSystemSpace(IN PVOID MappedBase)
4509 {
4510     DPRINT("MmUnmapViewInSystemSpace() called\n");
4511 
4512     return MmUnmapViewOfSegment(MmGetKernelAddressSpace(), MappedBase);
4513 }
4514 
4515 /**********************************************************************
4516  * NAME       EXPORTED
4517  *  MmCreateSection@
4518  *
4519  * DESCRIPTION
4520  *  Creates a section object.
4521  *
4522  * ARGUMENTS
4523  * SectionObject (OUT)
4524  *  Caller supplied storage for the resulting pointer
4525  *  to a SECTION_OBJECT instance;
4526  *
4527  * DesiredAccess
4528  *  Specifies the desired access to the section can be a
4529  *  combination of:
4530  *   STANDARD_RIGHTS_REQUIRED |
4531  *   SECTION_QUERY   |
4532  *   SECTION_MAP_WRITE  |
4533  *   SECTION_MAP_READ  |
4534  *   SECTION_MAP_EXECUTE
4535  *
4536  * ObjectAttributes [OPTIONAL]
4537  *  Initialized attributes for the object can be used
4538  *  to create a named section;
4539  *
4540  * MaximumSize
4541  *  Maximizes the size of the memory section. Must be
4542  *  non-NULL for a page-file backed section.
4543  *  If value specified for a mapped file and the file is
4544  *  not large enough, file will be extended.
4545  *
4546  * SectionPageProtection
4547  *  Can be a combination of:
4548  *   PAGE_READONLY |
4549  *   PAGE_READWRITE |
4550  *   PAGE_WRITEONLY |
4551  *   PAGE_WRITECOPY
4552  *
4553  * AllocationAttributes
4554  *  Can be a combination of:
4555  *   SEC_IMAGE |
4556  *   SEC_RESERVE
4557  *
4558  * FileHandle
4559  *  Handle to a file to create a section mapped to a file
4560  *  instead of a memory backed section;
4561  *
4562  * File
4563  *  Unknown.
4564  *
4565  * RETURN VALUE
4566  *  Status.
4567  *
4568  * @implemented
4569  */
4570 NTSTATUS NTAPI
4571 MmCreateSection (OUT PVOID  * Section,
4572                  IN ACCESS_MASK  DesiredAccess,
4573                  IN POBJECT_ATTRIBUTES ObjectAttributes     OPTIONAL,
4574                  IN PLARGE_INTEGER  MaximumSize,
4575                  IN ULONG   SectionPageProtection,
4576                  IN ULONG   AllocationAttributes,
4577                  IN HANDLE   FileHandle   OPTIONAL,
4578                  IN PFILE_OBJECT  FileObject  OPTIONAL)
4579 {
4580     NTSTATUS Status;
4581     ULONG Protection;
4582     PSECTION *SectionObject = (PSECTION *)Section;
4583     BOOLEAN FileLock = FALSE;
4584 
4585     /* Check if an ARM3 section is being created instead */
4586     if (!(AllocationAttributes & (SEC_IMAGE | SEC_PHYSICALMEMORY)))
4587     {
4588         if (!(FileObject) && !(FileHandle))
4589         {
4590             return MmCreateArm3Section(Section,
4591                                        DesiredAccess,
4592                                        ObjectAttributes,
4593                                        MaximumSize,
4594                                        SectionPageProtection,
4595                                        AllocationAttributes &~ 1,
4596                                        FileHandle,
4597                                        FileObject);
4598         }
4599     }
4600 
4601     /* Convert section flag to page flag */
4602     if (AllocationAttributes & SEC_NOCACHE) SectionPageProtection |= PAGE_NOCACHE;
4603 
4604     /* Check to make sure the protection is correct. Nt* does this already */
4605     Protection = MiMakeProtectionMask(SectionPageProtection);
4606     if (Protection == MM_INVALID_PROTECTION)
4607     {
4608         DPRINT1("Page protection is invalid\n");
4609         return STATUS_INVALID_PAGE_PROTECTION;
4610     }
4611 
4612     /* Check if this is going to be a data or image backed file section */
4613     if ((FileHandle) || (FileObject))
4614     {
4615         /* These cannot be mapped with large pages */
4616         if (AllocationAttributes & SEC_LARGE_PAGES)
4617         {
4618             DPRINT1("Large pages cannot be used with an image mapping\n");
4619             return STATUS_INVALID_PARAMETER_6;
4620         }
4621 
4622         /* Did the caller pass a file object ? */
4623         if (FileObject)
4624         {
4625             /* Reference the object directly */
4626             ObReferenceObject(FileObject);
4627 
4628             /* We don't create image mappings with file objects */
4629             AllocationAttributes &= ~SEC_IMAGE;
4630         }
4631         else
4632         {
4633             /* Reference the file handle to get the object */
4634             Status = ObReferenceObjectByHandle(FileHandle,
4635                                                MmMakeFileAccess[Protection],
4636                                                IoFileObjectType,
4637                                                ExGetPreviousMode(),
4638                                                (PVOID*)&FileObject,
4639                                                NULL);
4640             if (!NT_SUCCESS(Status))
4641             {
4642                 DPRINT1("Failed to get a handle to the FO: %lx\n", Status);
4643                 return Status;
4644             }
4645 
4646             /* Lock the file */
4647             Status = FsRtlAcquireToCreateMappedSection(FileObject, SectionPageProtection);
4648             if (!NT_SUCCESS(Status))
4649             {
4650                 ObDereferenceObject(FileObject);
4651                 return Status;
4652             }
4653 
4654             FileLock = TRUE;
4655 
4656             /* Deny access if there are writes on the file */
4657 #if 0
4658             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4659             {
4660                 DPRINT1("Cannot create image maps with writers open on the file!\n");
4661                 Status = STATUS_ACCESS_DENIED;
4662                 goto Quit;
4663             }
4664 #else
4665             if ((AllocationAttributes & SEC_IMAGE) && (Status == STATUS_FILE_LOCKED_WITH_WRITERS))
4666                 DPRINT1("Creating image map with writers open on the file!\n");
4667 #endif
4668         }
4669     }
4670     else
4671     {
4672         /* A handle must be supplied with SEC_IMAGE, as this is the no-handle path */
4673         if (AllocationAttributes & SEC_IMAGE) return STATUS_INVALID_FILE_FOR_SECTION;
4674     }
4675 
4676     if (AllocationAttributes & SEC_IMAGE)
4677     {
4678         Status = MmCreateImageSection(SectionObject,
4679                                       DesiredAccess,
4680                                       ObjectAttributes,
4681                                       MaximumSize,
4682                                       SectionPageProtection,
4683                                       AllocationAttributes,
4684                                       FileObject);
4685     }
4686 #ifndef NEWCC
4687     else if (FileObject != NULL)
4688     {
4689         Status =  MmCreateDataFileSection(SectionObject,
4690                                           DesiredAccess,
4691                                           ObjectAttributes,
4692                                           MaximumSize,
4693                                           SectionPageProtection,
4694                                           AllocationAttributes,
4695                                           FileObject,
4696                                           FileHandle != NULL);
4697     }
4698 #else
4699     else if (FileHandle != NULL || FileObject != NULL)
4700     {
4701         Status = MmCreateCacheSection(SectionObject,
4702                                       DesiredAccess,
4703                                       ObjectAttributes,
4704                                       MaximumSize,
4705                                       SectionPageProtection,
4706                                       AllocationAttributes,
4707                                       FileObject);
4708     }
4709 #endif
4710     else
4711     {
4712         /* All cases should be handled above */
4713         Status = STATUS_INVALID_PARAMETER;
4714     }
4715 
4716     if (FileLock)
4717         FsRtlReleaseFile(FileObject);
4718     if (FileObject)
4719         ObDereferenceObject(FileObject);
4720 
4721     return Status;
4722 }
4723 
4724 BOOLEAN
4725 NTAPI
4726 MmArePagesResident(
4727     _In_ PEPROCESS Process,
4728     _In_ PVOID Address,
4729     _In_ ULONG Length)
4730 {
4731     PMEMORY_AREA MemoryArea;
4732     BOOLEAN Ret = TRUE;
4733     PMM_SECTION_SEGMENT Segment;
4734     LARGE_INTEGER SegmentOffset, RangeEnd;
4735     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
4736 
4737     MmLockAddressSpace(AddressSpace);
4738 
4739     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
4740     if (MemoryArea == NULL)
4741     {
4742         MmUnlockAddressSpace(AddressSpace);
4743         return FALSE;
4744     }
4745 
4746     /* Only supported in old Mm for now */
4747     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
4748     /* For file mappings */
4749     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
4750 
4751     Segment = MemoryArea->SectionData.Segment;
4752     MmLockSectionSegment(Segment);
4753 
4754     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
4755             + MemoryArea->SectionData.ViewOffset;
4756     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
4757             + MemoryArea->SectionData.ViewOffset;
4758 
4759     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
4760     {
4761         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
4762         if ((Entry == 0) || IS_SWAP_FROM_SSE(Entry))
4763         {
4764             Ret = FALSE;
4765             break;
4766         }
4767         SegmentOffset.QuadPart += PAGE_SIZE;
4768     }
4769 
4770     MmUnlockSectionSegment(Segment);
4771 
4772     MmUnlockAddressSpace(AddressSpace);
4773     return Ret;
4774 }
4775 
4776 /* Like CcPurgeCache but for the in-memory segment */
4777 BOOLEAN
4778 NTAPI
4779 MmPurgeSegment(
4780     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4781     _In_opt_ PLARGE_INTEGER Offset,
4782     _In_ ULONG Length)
4783 {
4784     LARGE_INTEGER PurgeStart, PurgeEnd;
4785     PMM_SECTION_SEGMENT Segment;
4786 
4787     Segment = MiGrabDataSection(SectionObjectPointer);
4788     if (!Segment)
4789     {
4790         /* Nothing to purge */
4791         return TRUE;
4792     }
4793 
4794     PurgeStart.QuadPart = Offset ? Offset->QuadPart : 0LL;
4795     if (Length && Offset)
4796     {
4797         if (!NT_SUCCESS(RtlLongLongAdd(PurgeStart.QuadPart, Length, &PurgeEnd.QuadPart)))
4798             return FALSE;
4799     }
4800 
4801     MmLockSectionSegment(Segment);
4802 
4803     if (!Length || !Offset)
4804     {
4805         /* We must calculate the length for ourselves */
4806         /* FIXME: All of this is suboptimal */
4807         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4808         /* No page. Nothing to purge */
4809         if (!ElemCount)
4810         {
4811             MmUnlockSectionSegment(Segment);
4812             MmDereferenceSegment(Segment);
4813             return TRUE;
4814         }
4815 
4816         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4817         PurgeEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4818     }
4819 
4820     while (PurgeStart.QuadPart < PurgeEnd.QuadPart)
4821     {
4822         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &PurgeStart);
4823 
4824         if (Entry == 0)
4825         {
4826             PurgeStart.QuadPart += PAGE_SIZE;
4827             continue;
4828         }
4829 
4830         if (IS_SWAP_FROM_SSE(Entry))
4831         {
4832             ASSERT(SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY);
4833             /* The page is currently being read. Meaning someone will need it soon. Bad luck */
4834             MmUnlockSectionSegment(Segment);
4835             MmDereferenceSegment(Segment);
4836             return FALSE;
4837         }
4838 
4839         if (IS_WRITE_SSE(Entry))
4840         {
4841             /* We're trying to purge an entry which is being written. Restart this loop iteration */
4842             MmUnlockSectionSegment(Segment);
4843             KeDelayExecutionThread(KernelMode, FALSE, &TinyTime);
4844             MmLockSectionSegment(Segment);
4845             continue;
4846         }
4847 
4848         if (SHARE_COUNT_FROM_SSE(Entry) > 0)
4849         {
4850             /* This page is currently in use. Bad luck */
4851             MmUnlockSectionSegment(Segment);
4852             MmDereferenceSegment(Segment);
4853             return FALSE;
4854         }
4855 
4856         /* We can let this page go */
4857         MmSetPageEntrySectionSegment(Segment, &PurgeStart, 0);
4858         MmReleasePageMemoryConsumer(MC_USER, PFN_FROM_SSE(Entry));
4859 
4860         PurgeStart.QuadPart += PAGE_SIZE;
4861     }
4862 
4863     /* This page is currently in use. Bad luck */
4864     MmUnlockSectionSegment(Segment);
4865     MmDereferenceSegment(Segment);
4866     return TRUE;
4867 }
4868 
4869 NTSTATUS
4870 NTAPI
4871 MmMakeDataSectionResident(
4872     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4873     _In_ LONGLONG Offset,
4874     _In_ ULONG Length,
4875     _In_ PLARGE_INTEGER ValidDataLength)
4876 {
4877     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4878 
4879     /* There must be a segment for this call */
4880     ASSERT(Segment);
4881 
4882     NTSTATUS Status = MmMakeSegmentResident(Segment, Offset, Length, ValidDataLength, FALSE);
4883 
4884     MmDereferenceSegment(Segment);
4885 
4886     return Status;
4887 }
4888 
4889 NTSTATUS
4890 NTAPI
4891 MmFlushSegment(
4892     _In_ PSECTION_OBJECT_POINTERS SectionObjectPointer,
4893     _In_opt_ PLARGE_INTEGER Offset,
4894     _In_ ULONG Length,
4895     _Out_opt_ PIO_STATUS_BLOCK Iosb)
4896 {
4897     LARGE_INTEGER FlushStart, FlushEnd;
4898     NTSTATUS Status;
4899 
4900     if (Offset)
4901     {
4902         FlushStart = *Offset;
4903         Status = RtlLongLongAdd(FlushStart.QuadPart, Length, &FlushEnd.QuadPart);
4904         if (!NT_SUCCESS(Status))
4905             return Status;
4906     }
4907 
4908     if (Iosb)
4909         Iosb->Information = 0;
4910 
4911     PMM_SECTION_SEGMENT Segment = MiGrabDataSection(SectionObjectPointer);
4912     if (!Segment)
4913     {
4914         /* Nothing to flush */
4915         if (Iosb)
4916             Iosb->Status = STATUS_SUCCESS;
4917         return STATUS_SUCCESS;
4918     }
4919 
4920     ASSERT(*Segment->Flags & MM_DATAFILE_SEGMENT);
4921 
4922     MmLockSectionSegment(Segment);
4923 
4924     if (!Offset)
4925     {
4926         FlushStart.QuadPart = 0;
4927 
4928         /* FIXME: All of this is suboptimal */
4929         ULONG ElemCount = RtlNumberGenericTableElements(&Segment->PageTable);
4930         /* No page. Nothing to flush */
4931         if (!ElemCount)
4932         {
4933             MmUnlockSectionSegment(Segment);
4934             MmDereferenceSegment(Segment);
4935             if (Iosb)
4936             {
4937                 Iosb->Status = STATUS_SUCCESS;
4938                 Iosb->Information = 0;
4939             }
4940             return STATUS_SUCCESS;
4941         }
4942 
4943         PCACHE_SECTION_PAGE_TABLE PageTable = RtlGetElementGenericTable(&Segment->PageTable, ElemCount - 1);
4944         FlushEnd.QuadPart = PageTable->FileOffset.QuadPart + _countof(PageTable->PageEntries) * PAGE_SIZE;
4945     }
4946 
4947     FlushStart.QuadPart >>= PAGE_SHIFT;
4948     FlushStart.QuadPart <<= PAGE_SHIFT;
4949 
4950     while (FlushStart.QuadPart < FlushEnd.QuadPart)
4951     {
4952         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &FlushStart);
4953 
4954         if (IS_DIRTY_SSE(Entry))
4955         {
4956             MmCheckDirtySegment(Segment, &FlushStart, FALSE, FALSE);
4957 
4958             if (Iosb)
4959                 Iosb->Information += PAGE_SIZE;
4960         }
4961 
4962         FlushStart.QuadPart += PAGE_SIZE;
4963     }
4964 
4965     MmUnlockSectionSegment(Segment);
4966     MmDereferenceSegment(Segment);
4967 
4968     if (Iosb)
4969         Iosb->Status = STATUS_SUCCESS;
4970 
4971     return STATUS_SUCCESS;
4972 }
4973 
4974 _Requires_exclusive_lock_held_(Segment->Lock)
4975 BOOLEAN
4976 NTAPI
4977 MmCheckDirtySegment(
4978     PMM_SECTION_SEGMENT Segment,
4979     PLARGE_INTEGER Offset,
4980     BOOLEAN ForceDirty,
4981     BOOLEAN PageOut)
4982 {
4983     ULONG_PTR Entry;
4984     NTSTATUS Status;
4985     PFN_NUMBER Page;
4986 
4987     ASSERT(Segment->Locked);
4988 
4989     ASSERT((Offset->QuadPart % PAGE_SIZE) == 0);
4990 
4991     DPRINT("Checking segment for file %wZ at offset 0x%I64X.\n", &Segment->FileObject->FileName, Offset->QuadPart);
4992 
4993     Entry = MmGetPageEntrySectionSegment(Segment, Offset);
4994     if (Entry == 0)
4995         return FALSE;
4996 
4997     Page = PFN_FROM_SSE(Entry);
4998     if ((IS_DIRTY_SSE(Entry)) || ForceDirty)
4999     {
5000         BOOLEAN DirtyAgain;
5001 
5002         /*
5003          * We got a dirty entry. This path is for the shared data,
5004          * be-it regular file maps or shared sections of DLLs
5005          */
5006         ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) ||
5007                FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5008 
5009         /* Insert the cleaned entry back. Mark it as write in progress, and clear the dirty bit. */
5010         Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
5011         Entry = WRITE_SSE(Entry);
5012         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5013 
5014         MmUnlockSectionSegment(Segment);
5015 
5016         if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5017         {
5018             PERESOURCE ResourceToRelease = NULL;
5019             KIRQL OldIrql;
5020 
5021             /* We have to write it back to the file. Tell the FS driver who we are */
5022             if (PageOut)
5023             {
5024                 LARGE_INTEGER EndOffset = *Offset;
5025 
5026                 ASSERT(IoGetTopLevelIrp() == NULL);
5027 
5028                 /* We need to disable all APCs */
5029                 KeRaiseIrql(APC_LEVEL, &OldIrql);
5030 
5031                 EndOffset.QuadPart += PAGE_SIZE;
5032                 Status = FsRtlAcquireFileForModWriteEx(Segment->FileObject,
5033                                                        &EndOffset,
5034                                                        &ResourceToRelease);
5035                 if (NT_SUCCESS(Status))
5036                 {
5037                     IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
5038                 }
5039                 else
5040                 {
5041                     /* Make sure we will not try to release anything */
5042                     ResourceToRelease = NULL;
5043                 }
5044             }
5045             else
5046             {
5047                 /* We don't have to lock. Say this is success */
5048                 Status = STATUS_SUCCESS;
5049             }
5050 
5051             /* Go ahead and write the page, if previous locking succeeded */
5052             if (NT_SUCCESS(Status))
5053             {
5054                 DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
5055                         Offset->QuadPart, &Segment->FileObject->FileName, PageOut ? "TRUE" : "FALSE");
5056                 Status = MiWritePage(Segment, Offset->QuadPart, Page);
5057             }
5058 
5059             if (PageOut)
5060             {
5061                 IoSetTopLevelIrp(NULL);
5062                 if (ResourceToRelease != NULL)
5063                 {
5064                     FsRtlReleaseFileForModWrite(Segment->FileObject, ResourceToRelease);
5065                 }
5066                 KeLowerIrql(OldIrql);
5067             }
5068         }
5069         else
5070         {
5071             /* This must only be called by the page-out path */
5072             ASSERT(PageOut);
5073 
5074             /* And this must be for a shared section in a DLL */
5075             ASSERT(FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
5076 
5077             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5078             if (!SwapEntry)
5079             {
5080                 SwapEntry = MmAllocSwapPage();
5081             }
5082 
5083             if (SwapEntry)
5084             {
5085                 Status = MmWriteToSwapPage(SwapEntry, Page);
5086                 if (NT_SUCCESS(Status))
5087                 {
5088                     MmSetSavedSwapEntryPage(Page, SwapEntry);
5089                 }
5090                 else
5091                 {
5092                     MmFreeSwapPage(SwapEntry);
5093                 }
5094             }
5095             else
5096             {
5097                 DPRINT1("Failed to allocate a swap page!\n");
5098                 Status = STATUS_INSUFFICIENT_RESOURCES;
5099             }
5100         }
5101 
5102         MmLockSectionSegment(Segment);
5103 
5104         /* Get the entry again */
5105         Entry = MmGetPageEntrySectionSegment(Segment, Offset);
5106         ASSERT(PFN_FROM_SSE(Entry) == Page);
5107 
5108         if (!NT_SUCCESS(Status))
5109         {
5110             /* Damn, this failed. Consider this page as still dirty */
5111             DPRINT1("MiWritePage FAILED: Status 0x%08x!\n", Status);
5112             DirtyAgain = TRUE;
5113         }
5114         else
5115         {
5116             /* Check if someone dirtified this page while we were not looking */
5117             DirtyAgain = IS_DIRTY_SSE(Entry);
5118         }
5119 
5120         /* Drop the reference we got, deleting the write altogether. */
5121         Entry = MAKE_SSE(Page << PAGE_SHIFT, SHARE_COUNT_FROM_SSE(Entry) - 1);
5122         if (DirtyAgain)
5123         {
5124             Entry = DIRTY_SSE(Entry);
5125         }
5126         MmSetPageEntrySectionSegment(Segment, Offset, Entry);
5127     }
5128 
5129     /* Were this page hanging there just for the sake of being present ? */
5130     if (!IS_DIRTY_SSE(Entry) && (SHARE_COUNT_FROM_SSE(Entry) == 0) && PageOut)
5131     {
5132         ULONG_PTR NewEntry = 0;
5133         /* Restore the swap entry here */
5134         if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
5135         {
5136             SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
5137             if (SwapEntry)
5138                 NewEntry = MAKE_SWAP_SSE(SwapEntry);
5139         }
5140 
5141         /* Yes. Release it */
5142         MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
5143         MmReleasePageMemoryConsumer(MC_USER, Page);
5144         /* Tell the caller we released the page */
5145         return TRUE;
5146     }
5147 
5148     return FALSE;
5149 }
5150 
5151 NTSTATUS
5152 NTAPI
5153 MmMakePagesDirty(
5154     _In_ PEPROCESS Process,
5155     _In_ PVOID Address,
5156     _In_ ULONG Length)
5157 {
5158     PMEMORY_AREA MemoryArea;
5159     PMM_SECTION_SEGMENT Segment;
5160     LARGE_INTEGER SegmentOffset, RangeEnd;
5161     PMMSUPPORT AddressSpace = Process ? &Process->Vm : MmGetKernelAddressSpace();
5162 
5163     MmLockAddressSpace(AddressSpace);
5164 
5165     MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
5166     if (MemoryArea == NULL)
5167     {
5168         DPRINT1("Unable to find memory area at address %p.\n", Address);
5169         MmUnlockAddressSpace(AddressSpace);
5170         return STATUS_NOT_MAPPED_VIEW;
5171     }
5172 
5173     /* Only supported in old Mm for now */
5174     ASSERT(MemoryArea->Type == MEMORY_AREA_SECTION_VIEW);
5175     /* For file mappings */
5176     ASSERT(MemoryArea->VadNode.u.VadFlags.VadType != VadImageMap);
5177 
5178     Segment = MemoryArea->SectionData.Segment;
5179     MmLockSectionSegment(Segment);
5180 
5181     SegmentOffset.QuadPart = PAGE_ROUND_DOWN(Address) - MA_GetStartingAddress(MemoryArea)
5182             + MemoryArea->SectionData.ViewOffset;
5183     RangeEnd.QuadPart = PAGE_ROUND_UP((ULONG_PTR)Address + Length) - MA_GetStartingAddress(MemoryArea)
5184             + MemoryArea->SectionData.ViewOffset;
5185 
5186     DPRINT("MmMakePagesResident: Segment %p, 0x%I64x -> 0x%I64x\n", Segment, SegmentOffset.QuadPart, RangeEnd.QuadPart);
5187 
5188     while (SegmentOffset.QuadPart < RangeEnd.QuadPart)
5189     {
5190         ULONG_PTR Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5191 
5192         /* Let any pending read proceed */
5193         while (MM_IS_WAIT_PTE(Entry))
5194         {
5195             MmUnlockSectionSegment(Segment);
5196             MmUnlockAddressSpace(AddressSpace);
5197             YieldProcessor();
5198             MmLockAddressSpace(AddressSpace);
5199             MmLockSectionSegment(Segment);
5200             Entry = MmGetPageEntrySectionSegment(Segment, &SegmentOffset);
5201         }
5202 
5203         /* We are called from Cc, this can't be backed by the page files */
5204         ASSERT(!IS_SWAP_FROM_SSE(Entry));
5205 
5206         /* If there is no page there, there is nothing to make dirty */
5207         if (Entry != 0)
5208         {
5209             /* Dirtify the entry */
5210             MmSetPageEntrySectionSegment(Segment, &SegmentOffset, DIRTY_SSE(Entry));
5211         }
5212 
5213         SegmentOffset.QuadPart += PAGE_SIZE;
5214     }
5215 
5216     MmUnlockSectionSegment(Segment);
5217 
5218     MmUnlockAddressSpace(AddressSpace);
5219     return STATUS_SUCCESS;
5220 }
5221 
5222 NTSTATUS
5223 NTAPI
5224 MmExtendSection(
5225     _In_ PVOID _Section,
5226     _Inout_ PLARGE_INTEGER NewSize)
5227 {
5228     PSECTION Section = _Section;
5229 
5230     /* It makes no sense to extend an image mapping */
5231     if (Section->u.Flags.Image)
5232         return STATUS_SECTION_NOT_EXTENDED;
5233 
5234     /* Nor is it possible to extend a page file mapping */
5235     if (!Section->u.Flags.File)
5236         return STATUS_SECTION_NOT_EXTENDED;
5237 
5238     if (!MiIsRosSectionObject(Section))
5239         return STATUS_NOT_IMPLEMENTED;
5240 
5241     /* We just extend the sizes. Shrinking is a no-op ? */
5242     if (NewSize->QuadPart > Section->SizeOfSection.QuadPart)
5243     {
5244         PMM_SECTION_SEGMENT Segment = (PMM_SECTION_SEGMENT)Section->Segment;
5245         Section->SizeOfSection = *NewSize;
5246 
5247         if (!Section->u.Flags.Reserve)
5248         {
5249             MmLockSectionSegment(Segment);
5250             if (Segment->RawLength.QuadPart < NewSize->QuadPart)
5251             {
5252                 Segment->RawLength = *NewSize;
5253                 Segment->Length.QuadPart = (NewSize->QuadPart + PAGE_SIZE - 1) & ~((LONGLONG)PAGE_SIZE);
5254             }
5255             MmUnlockSectionSegment(Segment);
5256         }
5257     }
5258 
5259     return STATUS_SUCCESS;
5260 }
5261 
5262 /* EOF */
5263