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