[NTOS:MM] PeFmtCreateSection(): Improve AMD64 image failure case
[reactos.git] / ntoskrnl / mm / section.c
1 /*
2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/section.c
21 * PURPOSE: Implements section objects
22 *
23 * PROGRAMMERS: Rex Jolliff
24 * David Welch
25 * Eric Kohl
26 * Emanuele Aliberti
27 * Eugene Ingerman
28 * Casper Hornstrup
29 * KJK::Hyperion
30 * Guido de Jong
31 * Ge van Geldorp
32 * Royce Mitchell III
33 * Filip Navara
34 * Aleksey Bragin
35 * Jason Filby
36 * Thomas Weidenmueller
37 * Gunnar Andre' Dalsnes
38 * Mike Nordell
39 * Alex Ionescu
40 * Gregor Anich
41 * Steven Edwards
42 * Herve Poussineau
43 */
44
45 /* INCLUDES *****************************************************************/
46
47 #include <ntoskrnl.h>
48 #include <cache/newcc.h>
49 #include <cache/section/newmm.h>
50 #define NDEBUG
51 #include <debug.h>
52 #include <reactos/exeformat.h>
53
54 #if defined (ALLOC_PRAGMA)
55 #pragma alloc_text(INIT, MmCreatePhysicalMemorySection)
56 #pragma alloc_text(INIT, MmInitSectionImplementation)
57 #endif
58
59 #include "ARM3/miarm.h"
60
61 #undef MmSetPageEntrySectionSegment
62 #define MmSetPageEntrySectionSegment(S,O,E) do { \
63 DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
64 _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \
65 } while (0)
66
67 extern MMSESSION MmSession;
68
69 NTSTATUS
70 NTAPI
71 MiMapViewInSystemSpace(IN PVOID Section,
72 IN PVOID Session,
73 OUT PVOID *MappedBase,
74 IN OUT PSIZE_T ViewSize);
75
76 NTSTATUS
77 NTAPI
78 MmCreateArm3Section(OUT PVOID *SectionObject,
79 IN ACCESS_MASK DesiredAccess,
80 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
81 IN PLARGE_INTEGER InputMaximumSize,
82 IN ULONG SectionPageProtection,
83 IN ULONG AllocationAttributes,
84 IN HANDLE FileHandle OPTIONAL,
85 IN PFILE_OBJECT FileObject OPTIONAL);
86
87 NTSTATUS
88 NTAPI
89 MmMapViewOfArm3Section(IN PVOID SectionObject,
90 IN PEPROCESS Process,
91 IN OUT PVOID *BaseAddress,
92 IN ULONG_PTR ZeroBits,
93 IN SIZE_T CommitSize,
94 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
95 IN OUT PSIZE_T ViewSize,
96 IN SECTION_INHERIT InheritDisposition,
97 IN ULONG AllocationType,
98 IN ULONG Protect);
99
100 //
101 // PeFmtCreateSection depends on the following:
102 //
103 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
104 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
105
106 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
107 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
108 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
109
110 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
111 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
112 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
113 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
114 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
115 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
116 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
117 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
118 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
119
120 /* TYPES *********************************************************************/
121
122 typedef struct
123 {
124 PROS_SECTION_OBJECT Section;
125 PMM_SECTION_SEGMENT Segment;
126 LARGE_INTEGER Offset;
127 BOOLEAN WasDirty;
128 BOOLEAN Private;
129 PEPROCESS CallingProcess;
130 ULONG_PTR SectionEntry;
131 }
132 MM_SECTION_PAGEOUT_CONTEXT;
133
134 /* GLOBALS *******************************************************************/
135
136 POBJECT_TYPE MmSectionObjectType = NULL;
137
138 ULONG_PTR MmSubsectionBase;
139
140 static ULONG SectionCharacteristicsToProtect[16] =
141 {
142 PAGE_NOACCESS, /* 0 = NONE */
143 PAGE_NOACCESS, /* 1 = SHARED */
144 PAGE_EXECUTE, /* 2 = EXECUTABLE */
145 PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
146 PAGE_READONLY, /* 4 = READABLE */
147 PAGE_READONLY, /* 5 = READABLE, SHARED */
148 PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
149 PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
150 /*
151 * FIXME? do we really need the WriteCopy field in segments? can't we use
152 * PAGE_WRITECOPY here?
153 */
154 PAGE_READWRITE, /* 8 = WRITABLE */
155 PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
156 PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
157 PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
158 PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
159 PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
160 PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
161 PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
162 };
163
164 extern ULONG MmMakeFileAccess [];
165 ACCESS_MASK NTAPI MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection);
166 static GENERIC_MAPPING MmpSectionMapping =
167 {
168 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
169 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
170 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
171 SECTION_ALL_ACCESS
172 };
173
174
175 /* FUNCTIONS *****************************************************************/
176
177
178 /*
179 References:
180 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
181 File Format Specification", revision 6.0 (February 1999)
182 */
183 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
184 IN SIZE_T FileHeaderSize,
185 IN PVOID File,
186 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
187 OUT PULONG Flags,
188 IN PEXEFMT_CB_READ_FILE ReadFileCb,
189 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
190 {
191 NTSTATUS nStatus;
192 ULONG cbFileHeaderOffsetSize = 0;
193 ULONG cbSectionHeadersOffset = 0;
194 ULONG cbSectionHeadersSize;
195 ULONG cbSectionHeadersOffsetSize = 0;
196 ULONG cbOptHeaderSize;
197 ULONG cbHeadersSize = 0;
198 ULONG nSectionAlignment;
199 ULONG nFileAlignment;
200 ULONG_PTR ImageBase = 0;
201 const IMAGE_DOS_HEADER * pidhDosHeader;
202 const IMAGE_NT_HEADERS32 * pinhNtHeader;
203 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
204 const IMAGE_SECTION_HEADER * pishSectionHeaders;
205 PMM_SECTION_SEGMENT pssSegments;
206 LARGE_INTEGER lnOffset;
207 PVOID pBuffer;
208 SIZE_T nPrevVirtualEndOfSegment = 0;
209 ULONG nFileSizeOfHeaders = 0;
210 ULONG i;
211 ULONG AlignedLength;
212
213 ASSERT(FileHeader);
214 ASSERT(FileHeaderSize > 0);
215 ASSERT(File);
216 ASSERT(ImageSectionObject);
217 ASSERT(ReadFileCb);
218 ASSERT(AllocateSegmentsCb);
219
220 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
221
222 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
223
224 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
225
226 pBuffer = NULL;
227 pidhDosHeader = FileHeader;
228
229 /* DOS HEADER */
230 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
231
232 /* image too small to be an MZ executable */
233 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
234 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
235
236 /* no MZ signature */
237 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
238 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
239
240 /* NT HEADER */
241 nStatus = STATUS_INVALID_IMAGE_PROTECT;
242
243 /* not a Windows executable */
244 if(pidhDosHeader->e_lfanew <= 0)
245 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
246
247 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
248 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
249
250 if(FileHeaderSize < cbFileHeaderOffsetSize)
251 pinhNtHeader = NULL;
252 else
253 {
254 /*
255 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
256 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
257 */
258 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
259 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
260 }
261
262 /*
263 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
264 * need to read the header from the file
265 */
266 if(FileHeaderSize < cbFileHeaderOffsetSize ||
267 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
268 {
269 ULONG cbNtHeaderSize;
270 ULONG cbReadSize;
271 PVOID pData;
272
273 l_ReadHeaderFromFile:
274 cbNtHeaderSize = 0;
275 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
276
277 /* read the header from the file */
278 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
279
280 if(!NT_SUCCESS(nStatus))
281 {
282 NTSTATUS ReturnedStatus = nStatus;
283
284 /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
285 if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
286
287 DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
288 }
289
290 ASSERT(pData);
291 ASSERT(pBuffer);
292 ASSERT(cbReadSize > 0);
293
294 nStatus = STATUS_INVALID_IMAGE_FORMAT;
295
296 /* the buffer doesn't contain the file header */
297 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
298 DIE(("The file doesn't contain the PE file header\n"));
299
300 pinhNtHeader = pData;
301
302 /* object still not aligned: copy it to the beginning of the buffer */
303 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
304 {
305 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
306 RtlMoveMemory(pBuffer, pData, cbReadSize);
307 pinhNtHeader = pBuffer;
308 }
309
310 /* invalid NT header */
311 nStatus = STATUS_INVALID_IMAGE_PROTECT;
312
313 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
314 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
315
316 nStatus = STATUS_INVALID_IMAGE_FORMAT;
317
318 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
319 DIE(("The full NT header is too large\n"));
320
321 /* the buffer doesn't contain the whole NT header */
322 if(cbReadSize < cbNtHeaderSize)
323 DIE(("The file doesn't contain the full NT header\n"));
324 }
325 else
326 {
327 ULONG cbOptHeaderOffsetSize = 0;
328
329 nStatus = STATUS_INVALID_IMAGE_PROTECT;
330
331 /* don't trust an invalid NT header */
332 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
333 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
334
335 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
336 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
337
338 nStatus = STATUS_INVALID_IMAGE_FORMAT;
339
340 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
341 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
342
343 /* the buffer doesn't contain the whole NT header: read it from the file */
344 if(cbOptHeaderOffsetSize > FileHeaderSize)
345 goto l_ReadHeaderFromFile;
346 }
347
348 /* read information from the NT header */
349 piohOptHeader = &pinhNtHeader->OptionalHeader;
350 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
351
352 nStatus = STATUS_INVALID_IMAGE_FORMAT;
353
354 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
355 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
356
357 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
358
359 switch(piohOptHeader->Magic)
360 {
361 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
362 #ifndef _WIN64
363 nStatus = STATUS_INVALID_IMAGE_WIN_64;
364 DIE(("Win64 optional header, unsupported\n"));
365 #else
366 // Fall through.
367 #endif
368 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
369 break;
370 default:
371 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
372 }
373
374 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
375 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
376 {
377 /* See [1], section 3.4.2 */
378 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
379 {
380 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
381 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
382 }
383 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
384 DIE(("The section alignment is smaller than the file alignment\n"));
385
386 nSectionAlignment = piohOptHeader->SectionAlignment;
387 nFileAlignment = piohOptHeader->FileAlignment;
388
389 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
390 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
391 }
392 else
393 {
394 nSectionAlignment = PAGE_SIZE;
395 nFileAlignment = PAGE_SIZE;
396 }
397
398 ASSERT(IsPowerOf2(nSectionAlignment));
399 ASSERT(IsPowerOf2(nFileAlignment));
400
401 switch(piohOptHeader->Magic)
402 {
403 /* PE32 */
404 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
405 {
406 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
407 ImageBase = piohOptHeader->ImageBase;
408
409 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
410 ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
411
412 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
413 ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
414
415 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
416 ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
417
418 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
419 {
420 ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
421
422 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
423 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
424 {
425 ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
426 ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
427 }
428 }
429
430 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
431 {
432 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
433 piohOptHeader->AddressOfEntryPoint);
434 }
435
436 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
437 ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
438 else
439 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
440
441 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
442 {
443 if (piohOptHeader->AddressOfEntryPoint == 0)
444 {
445 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
446 }
447 }
448
449 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
450 ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
451
452 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
453 {
454 ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
455
456 /*
457 * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
458 * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
459 * magic to any binary.
460 *
461 * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
462 * but honestly that's not tested. It will also break them when running no ReactOS once we implement
463 * the SxS support -- at which point, duh, this should be removed.
464 *
465 * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
466 */
467 ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
468 }
469
470 break;
471 }
472 #ifdef _WIN64
473 /* PE64 */
474 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
475 {
476 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
477
478 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
479
480 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
481 {
482 ImageBase = pioh64OptHeader->ImageBase;
483 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
484 DIE(("ImageBase exceeds the address space\n"));
485 }
486
487 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
488 {
489 if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
490 DIE(("SizeOfImage exceeds the address space\n"));
491
492 ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
493 }
494
495 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
496 {
497 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
498 DIE(("SizeOfStackReserve exceeds the address space\n"));
499
500 ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
501 }
502
503 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
504 {
505 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
506 DIE(("SizeOfStackCommit exceeds the address space\n"));
507
508 ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
509 }
510
511 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
512 {
513 ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
514
515 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
516 RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
517 {
518 ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
519 ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
520 }
521 }
522
523 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
524 {
525 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
526 pioh64OptHeader->AddressOfEntryPoint);
527 }
528
529 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
530 ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
531 else
532 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
533
534 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
535 {
536 if (pioh64OptHeader->AddressOfEntryPoint == 0)
537 {
538 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
539 }
540 }
541
542 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
543 ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
544
545 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
546 ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
547
548 break;
549 }
550 #endif // _WIN64
551 }
552
553 /* [1], section 3.4.2 */
554 if((ULONG_PTR)ImageBase % 0x10000)
555 DIE(("ImageBase is not aligned on a 64KB boundary"));
556
557 ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
558 ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
559 ImageSectionObject->ImageInformation.GpValue = 0;
560 ImageSectionObject->ImageInformation.ZeroBits = 0;
561 ImageSectionObject->BasedAddress = (PVOID)ImageBase;
562
563 /* SECTION HEADERS */
564 nStatus = STATUS_INVALID_IMAGE_FORMAT;
565
566 /* see [1], section 3.3 */
567 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
568 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
569
570 /*
571 * the additional segment is for the file's headers. They need to be present for
572 * the benefit of the dynamic loader (to locate exports, defaults for thread
573 * parameters, resources, etc.)
574 */
575 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
576
577 /* file offset for the section headers */
578 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
579 DIE(("Offset overflow\n"));
580
581 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
582 DIE(("Offset overflow\n"));
583
584 /* size of the section headers */
585 ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
586 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
587
588 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
589 DIE(("Section headers too large\n"));
590
591 /* size of the executable's headers */
592 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
593 {
594 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
595 // DIE(("SizeOfHeaders is not aligned\n"));
596
597 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
598 DIE(("The section headers overflow SizeOfHeaders\n"));
599
600 cbHeadersSize = piohOptHeader->SizeOfHeaders;
601 }
602 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
603 DIE(("Overflow aligning the size of headers\n"));
604
605 if(pBuffer)
606 {
607 ExFreePool(pBuffer);
608 pBuffer = NULL;
609 }
610 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
611 /* WARNING: piohOptHeader IS NO LONGER USABLE */
612 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
613
614 if(FileHeaderSize < cbSectionHeadersOffsetSize)
615 pishSectionHeaders = NULL;
616 else
617 {
618 /*
619 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
620 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
621 */
622 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
623 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
624 }
625
626 /*
627 * the buffer doesn't contain the section headers, or the alignment is wrong:
628 * read the headers from the file
629 */
630 if(FileHeaderSize < cbSectionHeadersOffsetSize ||
631 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
632 {
633 PVOID pData;
634 ULONG cbReadSize;
635
636 lnOffset.QuadPart = cbSectionHeadersOffset;
637
638 /* read the header from the file */
639 nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
640
641 if(!NT_SUCCESS(nStatus))
642 DIE(("ReadFile failed with status %08X\n", nStatus));
643
644 ASSERT(pData);
645 ASSERT(pBuffer);
646 ASSERT(cbReadSize > 0);
647
648 nStatus = STATUS_INVALID_IMAGE_FORMAT;
649
650 /* the buffer doesn't contain all the section headers */
651 if(cbReadSize < cbSectionHeadersSize)
652 DIE(("The file doesn't contain all of the section headers\n"));
653
654 pishSectionHeaders = pData;
655
656 /* object still not aligned: copy it to the beginning of the buffer */
657 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
658 {
659 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
660 RtlMoveMemory(pBuffer, pData, cbReadSize);
661 pishSectionHeaders = pBuffer;
662 }
663 }
664
665 /* SEGMENTS */
666 /* allocate the segments */
667 nStatus = STATUS_INSUFFICIENT_RESOURCES;
668 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
669
670 if(ImageSectionObject->Segments == NULL)
671 DIE(("AllocateSegments failed\n"));
672
673 /* initialize the headers segment */
674 pssSegments = ImageSectionObject->Segments;
675
676 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
677
678 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
679 DIE(("Cannot align the size of the section headers\n"));
680
681 nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
682 if (nPrevVirtualEndOfSegment < cbHeadersSize)
683 DIE(("Cannot align the size of the section headers\n"));
684
685 pssSegments[0].Image.FileOffset = 0;
686 pssSegments[0].Protection = PAGE_READONLY;
687 pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
688 pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
689 pssSegments[0].Image.VirtualAddress = 0;
690 pssSegments[0].Image.Characteristics = 0;
691 pssSegments[0].WriteCopy = TRUE;
692
693 /* skip the headers segment */
694 ++ pssSegments;
695
696 nStatus = STATUS_INVALID_IMAGE_FORMAT;
697
698 /* convert the executable sections into segments. See also [1], section 4 */
699 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
700 {
701 ULONG nCharacteristics;
702
703 /* validate the alignment */
704 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
705 DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
706
707 /* sections must be contiguous, ordered by base address and non-overlapping */
708 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
709 DIE(("Memory gap between section %u and the previous\n", i));
710
711 /* ignore explicit BSS sections */
712 if(pishSectionHeaders[i].SizeOfRawData != 0)
713 {
714 /* validate the alignment */
715 #if 0
716 /* Yes, this should be a multiple of FileAlignment, but there's
717 * stuff out there that isn't. We can cope with that
718 */
719 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
720 DIE(("SizeOfRawData[%u] is not aligned\n", i));
721 #endif
722
723 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
724 // DIE(("PointerToRawData[%u] is not aligned\n", i));
725
726 /* conversion */
727 pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
728 pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
729 }
730 else
731 {
732 ASSERT(pssSegments[i].Image.FileOffset == 0);
733 ASSERT(pssSegments[i].RawLength.QuadPart == 0);
734 }
735
736 ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
737
738 nCharacteristics = pishSectionHeaders[i].Characteristics;
739
740 /* no explicit protection */
741 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
742 {
743 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
744 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
745
746 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
747 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
748
749 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
750 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
751 }
752
753 /* see table above */
754 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
755 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
756
757 if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
758 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
759 else
760 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
761
762 AlignedLength = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
763 if(AlignedLength < pssSegments[i].Length.LowPart)
764 DIE(("Cannot align the virtual size of section %u\n", i));
765
766 pssSegments[i].Length.LowPart = AlignedLength;
767
768 if(pssSegments[i].Length.QuadPart == 0)
769 DIE(("Virtual size of section %u is null\n", i));
770
771 pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
772 pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
773
774 /* ensure the memory image is no larger than 4GB */
775 nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
776 if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
777 DIE(("The image is too large\n"));
778 }
779
780 if(nSectionAlignment >= PAGE_SIZE)
781 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
782
783 /* Success */
784 nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
785
786 l_Return:
787 if(pBuffer)
788 ExFreePool(pBuffer);
789
790 return nStatus;
791 }
792
793 /*
794 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
795 * ARGUMENTS: PFILE_OBJECT to wait for.
796 * RETURNS: Status of the wait.
797 */
798 NTSTATUS
799 MmspWaitForFileLock(PFILE_OBJECT File)
800 {
801 return STATUS_SUCCESS;
802 //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
803 }
804
805 VOID
806 NTAPI
807 MmFreeSectionSegments(PFILE_OBJECT FileObject)
808 {
809 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
810 {
811 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
812 PMM_SECTION_SEGMENT SectionSegments;
813 ULONG NrSegments;
814 ULONG i;
815
816 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
817 NrSegments = ImageSectionObject->NrSegments;
818 SectionSegments = ImageSectionObject->Segments;
819 for (i = 0; i < NrSegments; i++)
820 {
821 if (SectionSegments[i].ReferenceCount != 0)
822 {
823 DPRINT1("Image segment %lu still referenced (was %lu)\n", i,
824 SectionSegments[i].ReferenceCount);
825 KeBugCheck(MEMORY_MANAGEMENT);
826 }
827 MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
828 }
829 ExFreePool(ImageSectionObject->Segments);
830 ExFreePool(ImageSectionObject);
831 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
832 }
833 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
834 {
835 PMM_SECTION_SEGMENT Segment;
836
837 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
838 DataSectionObject;
839
840 if (Segment->ReferenceCount != 0)
841 {
842 DPRINT1("Data segment still referenced\n");
843 KeBugCheck(MEMORY_MANAGEMENT);
844 }
845 MmFreePageTablesSectionSegment(Segment, NULL);
846 ExFreePool(Segment);
847 FileObject->SectionObjectPointer->DataSectionObject = NULL;
848 }
849 }
850
851 VOID
852 NTAPI
853 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
854 PLARGE_INTEGER Offset)
855 {
856 ULONG_PTR Entry;
857
858 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
859 if (Entry == 0)
860 {
861 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
862 KeBugCheck(MEMORY_MANAGEMENT);
863 }
864 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
865 {
866 DPRINT1("Maximum share count reached\n");
867 KeBugCheck(MEMORY_MANAGEMENT);
868 }
869 if (IS_SWAP_FROM_SSE(Entry))
870 {
871 KeBugCheck(MEMORY_MANAGEMENT);
872 }
873 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
874 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
875 }
876
877 BOOLEAN
878 NTAPI
879 MmUnsharePageEntrySectionSegment(PROS_SECTION_OBJECT Section,
880 PMM_SECTION_SEGMENT Segment,
881 PLARGE_INTEGER Offset,
882 BOOLEAN Dirty,
883 BOOLEAN PageOut,
884 ULONG_PTR *InEntry)
885 {
886 ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
887 BOOLEAN IsDirectMapped = FALSE;
888
889 if (Entry == 0)
890 {
891 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
892 KeBugCheck(MEMORY_MANAGEMENT);
893 }
894 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
895 {
896 DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
897 KeBugCheck(MEMORY_MANAGEMENT);
898 }
899 if (IS_SWAP_FROM_SSE(Entry))
900 {
901 KeBugCheck(MEMORY_MANAGEMENT);
902 }
903 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
904 /*
905 * If we reducing the share count of this entry to zero then set the entry
906 * to zero and tell the cache the page is no longer mapped.
907 */
908 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
909 {
910 PFILE_OBJECT FileObject;
911 SWAPENTRY SavedSwapEntry;
912 PFN_NUMBER Page;
913 #ifndef NEWCC
914 PROS_SHARED_CACHE_MAP SharedCacheMap;
915 BOOLEAN IsImageSection;
916 LARGE_INTEGER FileOffset;
917
918 FileOffset.QuadPart = Offset->QuadPart + Segment->Image.FileOffset;
919 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
920 #endif
921
922 Page = PFN_FROM_SSE(Entry);
923 FileObject = Section->FileObject;
924 if (FileObject != NULL &&
925 !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
926 {
927
928 #ifndef NEWCC
929 if ((FileOffset.QuadPart % PAGE_SIZE) == 0 &&
930 (Offset->QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
931 {
932 NTSTATUS Status;
933 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
934 IsDirectMapped = TRUE;
935 #ifndef NEWCC
936 Status = CcRosUnmapVacb(SharedCacheMap, FileOffset.QuadPart, Dirty);
937 #else
938 Status = STATUS_SUCCESS;
939 #endif
940 if (!NT_SUCCESS(Status))
941 {
942 DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
943 KeBugCheck(MEMORY_MANAGEMENT);
944 }
945 }
946 #endif
947 }
948
949 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
950 if (SavedSwapEntry == 0)
951 {
952 if (!PageOut &&
953 ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
954 (Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)))
955 {
956 /*
957 * FIXME:
958 * Try to page out this page and set the swap entry
959 * within the section segment. There exist no rmap entry
960 * for this page. The pager thread can't page out a
961 * page without a rmap entry.
962 */
963 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
964 if (InEntry) *InEntry = Entry;
965 MiSetPageEvent(NULL, NULL);
966 }
967 else
968 {
969 MmSetPageEntrySectionSegment(Segment, Offset, 0);
970 if (InEntry) *InEntry = 0;
971 MiSetPageEvent(NULL, NULL);
972 if (!IsDirectMapped)
973 {
974 MmReleasePageMemoryConsumer(MC_USER, Page);
975 }
976 }
977 }
978 else
979 {
980 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
981 (Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
982 {
983 if (!PageOut)
984 {
985 if (Dirty)
986 {
987 /*
988 * FIXME:
989 * We hold all locks. Nobody can do something with the current
990 * process and the current segment (also not within an other process).
991 */
992 NTSTATUS Status;
993 Status = MmWriteToSwapPage(SavedSwapEntry, Page);
994 if (!NT_SUCCESS(Status))
995 {
996 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status);
997 KeBugCheck(MEMORY_MANAGEMENT);
998 }
999 }
1000 MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
1001 if (InEntry) *InEntry = MAKE_SWAP_SSE(SavedSwapEntry);
1002 MmSetSavedSwapEntryPage(Page, 0);
1003 MiSetPageEvent(NULL, NULL);
1004 }
1005 MmReleasePageMemoryConsumer(MC_USER, Page);
1006 }
1007 else
1008 {
1009 DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n");
1010 KeBugCheck(MEMORY_MANAGEMENT);
1011 }
1012 }
1013 }
1014 else
1015 {
1016 if (InEntry)
1017 *InEntry = Entry;
1018 else
1019 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1020 }
1021 return(SHARE_COUNT_FROM_SSE(Entry) > 0);
1022 }
1023
1024 BOOLEAN MiIsPageFromCache(PMEMORY_AREA MemoryArea,
1025 LONGLONG SegOffset)
1026 {
1027 #ifndef NEWCC
1028 if (!(MemoryArea->Data.SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1029 {
1030 PROS_SHARED_CACHE_MAP SharedCacheMap;
1031 PROS_VACB Vacb;
1032 SharedCacheMap = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
1033 Vacb = CcRosLookupVacb(SharedCacheMap, SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset);
1034 if (Vacb)
1035 {
1036 CcRosReleaseVacb(SharedCacheMap, Vacb, Vacb->Valid, FALSE, TRUE);
1037 return TRUE;
1038 }
1039 }
1040 #endif
1041 return FALSE;
1042 }
1043
1044 NTSTATUS
1045 NTAPI
1046 MiCopyFromUserPage(PFN_NUMBER DestPage, const VOID *SrcAddress)
1047 {
1048 PEPROCESS Process;
1049 KIRQL Irql;
1050 PVOID DestAddress;
1051
1052 Process = PsGetCurrentProcess();
1053 DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1054 if (DestAddress == NULL)
1055 {
1056 return(STATUS_NO_MEMORY);
1057 }
1058 ASSERT((ULONG_PTR)DestAddress % PAGE_SIZE == 0);
1059 ASSERT((ULONG_PTR)SrcAddress % PAGE_SIZE == 0);
1060 RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
1061 MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1062 return(STATUS_SUCCESS);
1063 }
1064
1065 #ifndef NEWCC
1066 NTSTATUS
1067 NTAPI
1068 MiReadPage(PMEMORY_AREA MemoryArea,
1069 LONGLONG SegOffset,
1070 PPFN_NUMBER Page)
1071 /*
1072 * FUNCTION: Read a page for a section backed memory area.
1073 * PARAMETERS:
1074 * MemoryArea - Memory area to read the page for.
1075 * Offset - Offset of the page to read.
1076 * Page - Variable that receives a page contains the read data.
1077 */
1078 {
1079 LONGLONG BaseOffset;
1080 LONGLONG FileOffset;
1081 PVOID BaseAddress;
1082 BOOLEAN UptoDate;
1083 PROS_VACB Vacb;
1084 PFILE_OBJECT FileObject;
1085 NTSTATUS Status;
1086 LONGLONG RawLength;
1087 PROS_SHARED_CACHE_MAP SharedCacheMap;
1088 BOOLEAN IsImageSection;
1089 LONGLONG Length;
1090
1091 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
1092 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1093 RawLength = MemoryArea->Data.SectionData.Segment->RawLength.QuadPart;
1094 FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset;
1095 IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1096
1097 ASSERT(SharedCacheMap);
1098
1099 DPRINT("%S %I64x\n", FileObject->FileName.Buffer, FileOffset);
1100
1101 /*
1102 * If the file system is letting us go directly to the cache and the
1103 * memory area was mapped at an offset in the file which is page aligned
1104 * then get the related VACB.
1105 */
1106 if (((FileOffset % PAGE_SIZE) == 0) &&
1107 ((SegOffset + PAGE_SIZE <= RawLength) || !IsImageSection) &&
1108 !(MemoryArea->Data.SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1109 {
1110
1111 /*
1112 * Get the related VACB; we use a lower level interface than
1113 * filesystems do because it is safe for us to use an offset with an
1114 * alignment less than the file system block size.
1115 */
1116 Status = CcRosGetVacb(SharedCacheMap,
1117 FileOffset,
1118 &BaseOffset,
1119 &BaseAddress,
1120 &UptoDate,
1121 &Vacb);
1122 if (!NT_SUCCESS(Status))
1123 {
1124 return(Status);
1125 }
1126 if (!UptoDate)
1127 {
1128 /*
1129 * If the VACB isn't up to date then call the file
1130 * system to read in the data.
1131 */
1132 Status = CcReadVirtualAddress(Vacb);
1133 if (!NT_SUCCESS(Status))
1134 {
1135 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1136 return Status;
1137 }
1138 }
1139
1140 /* Probe the page, since it's PDE might not be synced */
1141 (void)*((volatile char*)BaseAddress + FileOffset - BaseOffset);
1142
1143 /*
1144 * Retrieve the page from the view that we actually want.
1145 */
1146 (*Page) = MmGetPhysicalAddress((char*)BaseAddress +
1147 FileOffset - BaseOffset).LowPart >> PAGE_SHIFT;
1148
1149 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, TRUE);
1150 }
1151 else
1152 {
1153 PEPROCESS Process;
1154 KIRQL Irql;
1155 PVOID PageAddr;
1156 LONGLONG VacbOffset;
1157
1158 /*
1159 * Allocate a page, this is rather complicated by the possibility
1160 * we might have to move other things out of memory
1161 */
1162 MI_SET_USAGE(MI_USAGE_SECTION);
1163 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
1164 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
1165 if (!NT_SUCCESS(Status))
1166 {
1167 return(Status);
1168 }
1169 Status = CcRosGetVacb(SharedCacheMap,
1170 FileOffset,
1171 &BaseOffset,
1172 &BaseAddress,
1173 &UptoDate,
1174 &Vacb);
1175 if (!NT_SUCCESS(Status))
1176 {
1177 return(Status);
1178 }
1179 if (!UptoDate)
1180 {
1181 /*
1182 * If the VACB isn't up to date then call the file
1183 * system to read in the data.
1184 */
1185 Status = CcReadVirtualAddress(Vacb);
1186 if (!NT_SUCCESS(Status))
1187 {
1188 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1189 return Status;
1190 }
1191 }
1192
1193 Process = PsGetCurrentProcess();
1194 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1195 VacbOffset = BaseOffset + VACB_MAPPING_GRANULARITY - FileOffset;
1196 Length = RawLength - SegOffset;
1197 if (Length <= VacbOffset && Length <= PAGE_SIZE)
1198 {
1199 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
1200 }
1201 else if (VacbOffset >= PAGE_SIZE)
1202 {
1203 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
1204 }
1205 else
1206 {
1207 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, VacbOffset);
1208 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1209 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
1210 Status = CcRosGetVacb(SharedCacheMap,
1211 FileOffset + VacbOffset,
1212 &BaseOffset,
1213 &BaseAddress,
1214 &UptoDate,
1215 &Vacb);
1216 if (!NT_SUCCESS(Status))
1217 {
1218 return(Status);
1219 }
1220 if (!UptoDate)
1221 {
1222 /*
1223 * If the VACB isn't up to date then call the file
1224 * system to read in the data.
1225 */
1226 Status = CcReadVirtualAddress(Vacb);
1227 if (!NT_SUCCESS(Status))
1228 {
1229 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1230 return Status;
1231 }
1232 }
1233 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1234 if (Length < PAGE_SIZE)
1235 {
1236 memcpy((char*)PageAddr + VacbOffset, BaseAddress, Length - VacbOffset);
1237 }
1238 else
1239 {
1240 memcpy((char*)PageAddr + VacbOffset, BaseAddress, PAGE_SIZE - VacbOffset);
1241 }
1242 }
1243 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1244 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
1245 }
1246 return(STATUS_SUCCESS);
1247 }
1248 #else
1249 NTSTATUS
1250 NTAPI
1251 MiReadPage(PMEMORY_AREA MemoryArea,
1252 LONGLONG SegOffset,
1253 PPFN_NUMBER Page)
1254 /*
1255 * FUNCTION: Read a page for a section backed memory area.
1256 * PARAMETERS:
1257 * MemoryArea - Memory area to read the page for.
1258 * Offset - Offset of the page to read.
1259 * Page - Variable that receives a page contains the read data.
1260 */
1261 {
1262 MM_REQUIRED_RESOURCES Resources;
1263 NTSTATUS Status;
1264
1265 RtlZeroMemory(&Resources, sizeof(MM_REQUIRED_RESOURCES));
1266
1267 Resources.Context = MemoryArea->Data.SectionData.Section->FileObject;
1268 Resources.FileOffset.QuadPart = SegOffset +
1269 MemoryArea->Data.SectionData.Segment->Image.FileOffset;
1270 Resources.Consumer = MC_USER;
1271 Resources.Amount = PAGE_SIZE;
1272
1273 DPRINT("%S, offset 0x%x, len 0x%x, page 0x%x\n", ((PFILE_OBJECT)Resources.Context)->FileName.Buffer, Resources.FileOffset.LowPart, Resources.Amount, Resources.Page[0]);
1274
1275 Status = MiReadFilePage(MmGetKernelAddressSpace(), MemoryArea, &Resources);
1276 *Page = Resources.Page[0];
1277 return Status;
1278 }
1279 #endif
1280
1281 static VOID
1282 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
1283 PVOID BaseAddress,
1284 SIZE_T RegionSize,
1285 ULONG OldType,
1286 ULONG OldProtect,
1287 ULONG NewType,
1288 ULONG NewProtect)
1289 {
1290 PMEMORY_AREA MemoryArea;
1291 PMM_SECTION_SEGMENT Segment;
1292 BOOLEAN DoCOW = FALSE;
1293 ULONG i;
1294 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1295
1296 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
1297 ASSERT(MemoryArea != NULL);
1298 Segment = MemoryArea->Data.SectionData.Segment;
1299 MmLockSectionSegment(Segment);
1300
1301 if ((Segment->WriteCopy) &&
1302 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
1303 {
1304 DoCOW = TRUE;
1305 }
1306
1307 if (OldProtect != NewProtect)
1308 {
1309 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
1310 {
1311 SWAPENTRY SwapEntry;
1312 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
1313 ULONG Protect = NewProtect;
1314
1315 /* Wait for a wait entry to disappear */
1316 do
1317 {
1318 MmGetPageFileMapping(Process, Address, &SwapEntry);
1319 if (SwapEntry != MM_WAIT_ENTRY)
1320 break;
1321 MiWaitForPageEvent(Process, Address);
1322 }
1323 while (TRUE);
1324
1325 /*
1326 * If we doing COW for this segment then check if the page is
1327 * already private.
1328 */
1329 if (DoCOW && MmIsPagePresent(Process, Address))
1330 {
1331 LARGE_INTEGER Offset;
1332 ULONG_PTR Entry;
1333 PFN_NUMBER Page;
1334
1335 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1336 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1337 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1338 /*
1339 * An MM_WAIT_ENTRY is ok in this case... It'll just count as
1340 * IS_SWAP_FROM_SSE and we'll do the right thing.
1341 */
1342 Page = MmGetPfnForProcess(Process, Address);
1343
1344 Protect = PAGE_READONLY;
1345 if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
1346 {
1347 Protect = NewProtect;
1348 }
1349 }
1350
1351 if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
1352 {
1353 MmSetPageProtect(Process, Address,
1354 Protect);
1355 }
1356 }
1357 }
1358
1359 MmUnlockSectionSegment(Segment);
1360 }
1361
1362 NTSTATUS
1363 NTAPI
1364 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1365 MEMORY_AREA* MemoryArea,
1366 PVOID Address,
1367 BOOLEAN Locked)
1368 {
1369 LARGE_INTEGER Offset;
1370 PFN_NUMBER Page;
1371 NTSTATUS Status;
1372 PROS_SECTION_OBJECT Section;
1373 PMM_SECTION_SEGMENT Segment;
1374 ULONG_PTR Entry;
1375 ULONG_PTR Entry1;
1376 ULONG Attributes;
1377 PMM_REGION Region;
1378 BOOLEAN HasSwapEntry;
1379 PVOID PAddress;
1380 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1381 SWAPENTRY SwapEntry;
1382
1383 /*
1384 * There is a window between taking the page fault and locking the
1385 * address space when another thread could load the page so we check
1386 * that.
1387 */
1388 if (MmIsPagePresent(Process, Address))
1389 {
1390 return(STATUS_SUCCESS);
1391 }
1392
1393 if (MmIsDisabledPage(Process, Address))
1394 {
1395 return(STATUS_ACCESS_VIOLATION);
1396 }
1397
1398 /*
1399 * Check for the virtual memory area being deleted.
1400 */
1401 if (MemoryArea->DeleteInProgress)
1402 {
1403 return(STATUS_UNSUCCESSFUL);
1404 }
1405
1406 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1407 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1408 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1409
1410 Segment = MemoryArea->Data.SectionData.Segment;
1411 Section = MemoryArea->Data.SectionData.Section;
1412 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1413 &MemoryArea->Data.SectionData.RegionListHead,
1414 Address, NULL);
1415 ASSERT(Region != NULL);
1416
1417 /* Check for a NOACCESS mapping */
1418 if (Region->Protect & PAGE_NOACCESS)
1419 {
1420 return STATUS_ACCESS_VIOLATION;
1421 }
1422
1423 if (Region->Protect & PAGE_GUARD)
1424 {
1425 /* Remove it */
1426 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
1427 &MemoryArea->Data.SectionData.RegionListHead,
1428 Address, PAGE_SIZE, Region->Type, Region->Protect & ~PAGE_GUARD,
1429 MmAlterViewAttributes);
1430
1431 if (!NT_SUCCESS(Status))
1432 {
1433 DPRINT1("Removing PAGE_GUARD protection failed : 0x%08x.\n", Status);
1434 }
1435
1436 return STATUS_GUARD_PAGE_VIOLATION;
1437 }
1438
1439 /*
1440 * Lock the segment
1441 */
1442 MmLockSectionSegment(Segment);
1443 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1444 /*
1445 * Check if this page needs to be mapped COW
1446 */
1447 if ((Segment->WriteCopy) &&
1448 (Region->Protect == PAGE_READWRITE ||
1449 Region->Protect == PAGE_EXECUTE_READWRITE))
1450 {
1451 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1452 }
1453 else
1454 {
1455 Attributes = Region->Protect;
1456 }
1457
1458 /*
1459 * Check if someone else is already handling this fault, if so wait
1460 * for them
1461 */
1462 if (Entry && MM_IS_WAIT_PTE(Entry))
1463 {
1464 MmUnlockSectionSegment(Segment);
1465 MmUnlockAddressSpace(AddressSpace);
1466 MiWaitForPageEvent(NULL, NULL);
1467 MmLockAddressSpace(AddressSpace);
1468 DPRINT("Address 0x%p\n", Address);
1469 return(STATUS_MM_RESTART_OPERATION);
1470 }
1471
1472 HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1473
1474 /* See if we should use a private page */
1475 if (HasSwapEntry)
1476 {
1477 SWAPENTRY DummyEntry;
1478
1479 /*
1480 * Is it a wait entry?
1481 */
1482 if (HasSwapEntry)
1483 {
1484 MmGetPageFileMapping(Process, Address, &SwapEntry);
1485
1486 if (SwapEntry == MM_WAIT_ENTRY)
1487 {
1488 MmUnlockSectionSegment(Segment);
1489 MmUnlockAddressSpace(AddressSpace);
1490 MiWaitForPageEvent(NULL, NULL);
1491 MmLockAddressSpace(AddressSpace);
1492 return STATUS_MM_RESTART_OPERATION;
1493 }
1494
1495 /*
1496 * Must be private page we have swapped out.
1497 */
1498
1499 /*
1500 * Sanity check
1501 */
1502 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
1503 {
1504 DPRINT1("Found a swaped out private page in a pagefile section.\n");
1505 KeBugCheck(MEMORY_MANAGEMENT);
1506 }
1507 MmDeletePageFileMapping(Process, Address, &SwapEntry);
1508 }
1509
1510 MmUnlockSectionSegment(Segment);
1511
1512 /* Tell everyone else we are serving the fault. */
1513 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1514
1515 MmUnlockAddressSpace(AddressSpace);
1516 MI_SET_USAGE(MI_USAGE_SECTION);
1517 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1518 if (!Process) MI_SET_PROCESS2("Kernel Section");
1519 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1520 if (!NT_SUCCESS(Status))
1521 {
1522 KeBugCheck(MEMORY_MANAGEMENT);
1523 }
1524
1525 if (HasSwapEntry)
1526 {
1527 Status = MmReadFromSwapPage(SwapEntry, Page);
1528 if (!NT_SUCCESS(Status))
1529 {
1530 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1531 KeBugCheck(MEMORY_MANAGEMENT);
1532 }
1533 }
1534
1535 MmLockAddressSpace(AddressSpace);
1536 MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1537 Status = MmCreateVirtualMapping(Process,
1538 PAddress,
1539 Region->Protect,
1540 &Page,
1541 1);
1542 if (!NT_SUCCESS(Status))
1543 {
1544 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1545 KeBugCheck(MEMORY_MANAGEMENT);
1546 return(Status);
1547 }
1548
1549 /*
1550 * Store the swap entry for later use.
1551 */
1552 if (HasSwapEntry)
1553 MmSetSavedSwapEntryPage(Page, SwapEntry);
1554
1555 /*
1556 * Add the page to the process's working set
1557 */
1558 MmInsertRmap(Page, Process, Address);
1559 /*
1560 * Finish the operation
1561 */
1562 MiSetPageEvent(Process, Address);
1563 DPRINT("Address 0x%p\n", Address);
1564 return(STATUS_SUCCESS);
1565 }
1566
1567 /*
1568 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1569 */
1570 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1571 {
1572 MmUnlockSectionSegment(Segment);
1573 /*
1574 * Just map the desired physical page
1575 */
1576 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1577 Status = MmCreateVirtualMappingUnsafe(Process,
1578 PAddress,
1579 Region->Protect,
1580 &Page,
1581 1);
1582 if (!NT_SUCCESS(Status))
1583 {
1584 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1585 KeBugCheck(MEMORY_MANAGEMENT);
1586 return(Status);
1587 }
1588
1589 /*
1590 * Cleanup and release locks
1591 */
1592 MiSetPageEvent(Process, Address);
1593 DPRINT("Address 0x%p\n", Address);
1594 return(STATUS_SUCCESS);
1595 }
1596
1597 /*
1598 * Get the entry corresponding to the offset within the section
1599 */
1600 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1601
1602 if (Entry == 0)
1603 {
1604 SWAPENTRY FakeSwapEntry;
1605
1606 /*
1607 * If the entry is zero (and it can't change because we have
1608 * locked the segment) then we need to load the page.
1609 */
1610
1611 /*
1612 * Release all our locks and read in the page from disk
1613 */
1614 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1615 MmUnlockSectionSegment(Segment);
1616 MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY);
1617 MmUnlockAddressSpace(AddressSpace);
1618
1619 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1620 ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart) &&
1621 (Section->AllocationAttributes & SEC_IMAGE))))
1622 {
1623 MI_SET_USAGE(MI_USAGE_SECTION);
1624 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1625 if (!Process) MI_SET_PROCESS2("Kernel Section");
1626 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1627 if (!NT_SUCCESS(Status))
1628 {
1629 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
1630 }
1631
1632 }
1633 else
1634 {
1635 Status = MiReadPage(MemoryArea, Offset.QuadPart, &Page);
1636 if (!NT_SUCCESS(Status))
1637 {
1638 DPRINT1("MiReadPage failed (Status %x)\n", Status);
1639 }
1640 }
1641 if (!NT_SUCCESS(Status))
1642 {
1643 /*
1644 * FIXME: What do we know in this case?
1645 */
1646 /*
1647 * Cleanup and release locks
1648 */
1649 MmLockAddressSpace(AddressSpace);
1650 MiSetPageEvent(Process, Address);
1651 DPRINT("Address 0x%p\n", Address);
1652 return(Status);
1653 }
1654
1655 /* Lock both segment and process address space while we proceed. */
1656 MmLockAddressSpace(AddressSpace);
1657 MmLockSectionSegment(Segment);
1658
1659 MmDeletePageFileMapping(Process, PAddress, &FakeSwapEntry);
1660 DPRINT("CreateVirtualMapping Page %x Process %p PAddress %p Attributes %x\n",
1661 Page, Process, PAddress, Attributes);
1662 Status = MmCreateVirtualMapping(Process,
1663 PAddress,
1664 Attributes,
1665 &Page,
1666 1);
1667 if (!NT_SUCCESS(Status))
1668 {
1669 DPRINT1("Unable to create virtual mapping\n");
1670 KeBugCheck(MEMORY_MANAGEMENT);
1671 }
1672 ASSERT(MmIsPagePresent(Process, PAddress));
1673 MmInsertRmap(Page, Process, Address);
1674
1675 /* Set this section offset has being backed by our new page. */
1676 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1677 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1678 MmUnlockSectionSegment(Segment);
1679
1680 MiSetPageEvent(Process, Address);
1681 DPRINT("Address 0x%p\n", Address);
1682 return(STATUS_SUCCESS);
1683 }
1684 else if (IS_SWAP_FROM_SSE(Entry))
1685 {
1686 SWAPENTRY SwapEntry;
1687
1688 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1689
1690 /* See if a page op is running on this segment. */
1691 if (SwapEntry == MM_WAIT_ENTRY)
1692 {
1693 MmUnlockSectionSegment(Segment);
1694 MmUnlockAddressSpace(AddressSpace);
1695 MiWaitForPageEvent(NULL, NULL);
1696 MmLockAddressSpace(AddressSpace);
1697 return STATUS_MM_RESTART_OPERATION;
1698 }
1699
1700 /*
1701 * Release all our locks and read in the page from disk
1702 */
1703 MmUnlockSectionSegment(Segment);
1704
1705 MmUnlockAddressSpace(AddressSpace);
1706 MI_SET_USAGE(MI_USAGE_SECTION);
1707 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1708 if (!Process) MI_SET_PROCESS2("Kernel Section");
1709 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1710 if (!NT_SUCCESS(Status))
1711 {
1712 KeBugCheck(MEMORY_MANAGEMENT);
1713 }
1714
1715 Status = MmReadFromSwapPage(SwapEntry, Page);
1716 if (!NT_SUCCESS(Status))
1717 {
1718 KeBugCheck(MEMORY_MANAGEMENT);
1719 }
1720
1721 /*
1722 * Relock the address space and segment
1723 */
1724 MmLockAddressSpace(AddressSpace);
1725 MmLockSectionSegment(Segment);
1726
1727 /*
1728 * Check the entry. No one should change the status of a page
1729 * that has a pending page-in.
1730 */
1731 Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
1732 if (Entry != Entry1)
1733 {
1734 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1735 KeBugCheck(MEMORY_MANAGEMENT);
1736 }
1737
1738 /*
1739 * Save the swap entry.
1740 */
1741 MmSetSavedSwapEntryPage(Page, SwapEntry);
1742
1743 /* Map the page into the process address space */
1744 Status = MmCreateVirtualMapping(Process,
1745 PAddress,
1746 Region->Protect,
1747 &Page,
1748 1);
1749 if (!NT_SUCCESS(Status))
1750 {
1751 DPRINT1("Unable to create virtual mapping\n");
1752 KeBugCheck(MEMORY_MANAGEMENT);
1753 }
1754 MmInsertRmap(Page, Process, Address);
1755
1756 /*
1757 * Mark the offset within the section as having valid, in-memory
1758 * data
1759 */
1760 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1761 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1762 MmUnlockSectionSegment(Segment);
1763
1764 MiSetPageEvent(Process, Address);
1765 DPRINT("Address 0x%p\n", Address);
1766 return(STATUS_SUCCESS);
1767 }
1768 else
1769 {
1770 /* We already have a page on this section offset. Map it into the process address space. */
1771 Page = PFN_FROM_SSE(Entry);
1772
1773 Status = MmCreateVirtualMapping(Process,
1774 PAddress,
1775 Attributes,
1776 &Page,
1777 1);
1778 if (!NT_SUCCESS(Status))
1779 {
1780 DPRINT1("Unable to create virtual mapping\n");
1781 KeBugCheck(MEMORY_MANAGEMENT);
1782 }
1783 MmInsertRmap(Page, Process, Address);
1784
1785 /* Take a reference on it */
1786 MmSharePageEntrySectionSegment(Segment, &Offset);
1787 MmUnlockSectionSegment(Segment);
1788
1789 MiSetPageEvent(Process, Address);
1790 DPRINT("Address 0x%p\n", Address);
1791 return(STATUS_SUCCESS);
1792 }
1793 }
1794
1795 NTSTATUS
1796 NTAPI
1797 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1798 MEMORY_AREA* MemoryArea,
1799 PVOID Address)
1800 {
1801 PMM_SECTION_SEGMENT Segment;
1802 PROS_SECTION_OBJECT Section;
1803 PFN_NUMBER OldPage;
1804 PFN_NUMBER NewPage;
1805 NTSTATUS Status;
1806 PVOID PAddress;
1807 LARGE_INTEGER Offset;
1808 PMM_REGION Region;
1809 ULONG_PTR Entry;
1810 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1811
1812 DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1813
1814 /* Make sure we have a page mapping for this address. */
1815 Status = MmNotPresentFaultSectionView(AddressSpace, MemoryArea, Address, TRUE);
1816 if (!NT_SUCCESS(Status))
1817 {
1818 /* This is invalid access ! */
1819 return Status;
1820 }
1821
1822 /*
1823 * Check if the page has already been set readwrite
1824 */
1825 if (MmGetPageProtect(Process, Address) & PAGE_READWRITE)
1826 {
1827 DPRINT("Address 0x%p\n", Address);
1828 return(STATUS_SUCCESS);
1829 }
1830
1831 /*
1832 * Find the offset of the page
1833 */
1834 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1835 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1836 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1837
1838 Segment = MemoryArea->Data.SectionData.Segment;
1839 Section = MemoryArea->Data.SectionData.Section;
1840 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1841 &MemoryArea->Data.SectionData.RegionListHead,
1842 Address, NULL);
1843 ASSERT(Region != NULL);
1844
1845 /*
1846 * Check if we are doing COW
1847 */
1848 if (!((Segment->WriteCopy) &&
1849 (Region->Protect == PAGE_READWRITE ||
1850 Region->Protect == PAGE_EXECUTE_READWRITE)))
1851 {
1852 DPRINT("Address 0x%p\n", Address);
1853 return(STATUS_ACCESS_VIOLATION);
1854 }
1855
1856 /* Get the page mapping this section offset. */
1857 MmLockSectionSegment(Segment);
1858 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1859
1860 /* Get the current page mapping for the process */
1861 ASSERT(MmIsPagePresent(Process, PAddress));
1862 OldPage = MmGetPfnForProcess(Process, PAddress);
1863 ASSERT(OldPage != 0);
1864
1865 if (IS_SWAP_FROM_SSE(Entry) ||
1866 PFN_FROM_SSE(Entry) != OldPage)
1867 {
1868 MmUnlockSectionSegment(Segment);
1869 /* This is a private page. We must only change the page protection. */
1870 MmSetPageProtect(Process, PAddress, Region->Protect);
1871 return(STATUS_SUCCESS);
1872 }
1873
1874 /*
1875 * Allocate a page
1876 */
1877 MI_SET_USAGE(MI_USAGE_SECTION);
1878 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1879 if (!Process) MI_SET_PROCESS2("Kernel Section");
1880 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1881 if (!NT_SUCCESS(Status))
1882 {
1883 KeBugCheck(MEMORY_MANAGEMENT);
1884 }
1885
1886 /*
1887 * Copy the old page
1888 */
1889 NT_VERIFY(NT_SUCCESS(MiCopyFromUserPage(NewPage, PAddress)));
1890
1891 /*
1892 * Unshare the old page.
1893 */
1894 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
1895 MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
1896 MmDeleteRmap(OldPage, Process, PAddress);
1897 MmUnsharePageEntrySectionSegment(Section, Segment, &Offset, FALSE, FALSE, NULL);
1898 MmUnlockSectionSegment(Segment);
1899
1900 /*
1901 * Set the PTE to point to the new page
1902 */
1903 Status = MmCreateVirtualMapping(Process,
1904 PAddress,
1905 Region->Protect,
1906 &NewPage,
1907 1);
1908 if (!NT_SUCCESS(Status))
1909 {
1910 DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
1911 KeBugCheck(MEMORY_MANAGEMENT);
1912 return(Status);
1913 }
1914 MmInsertRmap(NewPage, Process, PAddress);
1915
1916 MiSetPageEvent(Process, Address);
1917 DPRINT("Address 0x%p\n", Address);
1918 return(STATUS_SUCCESS);
1919 }
1920
1921 VOID
1922 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1923 {
1924 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1925 BOOLEAN WasDirty;
1926 PFN_NUMBER Page = 0;
1927
1928 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1929 if (Process)
1930 {
1931 MmLockAddressSpace(&Process->Vm);
1932 }
1933
1934 MmDeleteVirtualMapping(Process,
1935 Address,
1936 &WasDirty,
1937 &Page);
1938 if (WasDirty)
1939 {
1940 PageOutContext->WasDirty = TRUE;
1941 }
1942 if (!PageOutContext->Private)
1943 {
1944 MmLockSectionSegment(PageOutContext->Segment);
1945 MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section,
1946 PageOutContext->Segment,
1947 &PageOutContext->Offset,
1948 PageOutContext->WasDirty,
1949 TRUE,
1950 &PageOutContext->SectionEntry);
1951 MmUnlockSectionSegment(PageOutContext->Segment);
1952 }
1953 if (Process)
1954 {
1955 MmUnlockAddressSpace(&Process->Vm);
1956 }
1957
1958 if (PageOutContext->Private)
1959 {
1960 MmReleasePageMemoryConsumer(MC_USER, Page);
1961 }
1962 }
1963
1964 NTSTATUS
1965 NTAPI
1966 MmPageOutSectionView(PMMSUPPORT AddressSpace,
1967 MEMORY_AREA* MemoryArea,
1968 PVOID Address, ULONG_PTR Entry)
1969 {
1970 PFN_NUMBER Page;
1971 MM_SECTION_PAGEOUT_CONTEXT Context;
1972 SWAPENTRY SwapEntry;
1973 NTSTATUS Status;
1974 #ifndef NEWCC
1975 ULONGLONG FileOffset;
1976 PFILE_OBJECT FileObject;
1977 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
1978 BOOLEAN IsImageSection;
1979 #endif
1980 BOOLEAN DirectMapped;
1981 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1982 KIRQL OldIrql;
1983
1984 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1985
1986 /*
1987 * Get the segment and section.
1988 */
1989 Context.Segment = MemoryArea->Data.SectionData.Segment;
1990 Context.Section = MemoryArea->Data.SectionData.Section;
1991 Context.SectionEntry = Entry;
1992 Context.CallingProcess = Process;
1993
1994 Context.Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1995 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1996
1997 DirectMapped = FALSE;
1998
1999 MmLockSectionSegment(Context.Segment);
2000
2001 #ifndef NEWCC
2002 FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset;
2003 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2004 FileObject = Context.Section->FileObject;
2005
2006 if (FileObject != NULL &&
2007 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2008 {
2009 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
2010
2011 /*
2012 * If the file system is letting us go directly to the cache and the
2013 * memory area was mapped at an offset in the file which is page aligned
2014 * then note this is a direct mapped page.
2015 */
2016 if ((FileOffset % PAGE_SIZE) == 0 &&
2017 (Context.Offset.QuadPart + PAGE_SIZE <= Context.Segment->RawLength.QuadPart || !IsImageSection))
2018 {
2019 DirectMapped = TRUE;
2020 }
2021 }
2022 #endif
2023
2024
2025 /*
2026 * This should never happen since mappings of physical memory are never
2027 * placed in the rmap lists.
2028 */
2029 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2030 {
2031 DPRINT1("Trying to page out from physical memory section address 0x%p "
2032 "process %p\n", Address,
2033 Process ? Process->UniqueProcessId : 0);
2034 KeBugCheck(MEMORY_MANAGEMENT);
2035 }
2036
2037 /*
2038 * Get the section segment entry and the physical address.
2039 */
2040 if (!MmIsPagePresent(Process, Address))
2041 {
2042 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
2043 Process ? Process->UniqueProcessId : 0, Address);
2044 KeBugCheck(MEMORY_MANAGEMENT);
2045 }
2046 Page = MmGetPfnForProcess(Process, Address);
2047 SwapEntry = MmGetSavedSwapEntryPage(Page);
2048
2049 /*
2050 * Check the reference count to ensure this page can be paged out
2051 */
2052 if (MmGetReferenceCountPage(Page) != 1)
2053 {
2054 DPRINT("Cannot page out locked section page: 0x%lu (RefCount: %lu)\n",
2055 Page, MmGetReferenceCountPage(Page));
2056 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2057 MmUnlockSectionSegment(Context.Segment);
2058 return STATUS_UNSUCCESSFUL;
2059 }
2060
2061 /*
2062 * Prepare the context structure for the rmap delete call.
2063 */
2064 MmUnlockSectionSegment(Context.Segment);
2065 Context.WasDirty = FALSE;
2066 if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
2067 {
2068 Context.Private = TRUE;
2069 }
2070 else
2071 {
2072 Context.Private = FALSE;
2073 }
2074
2075 /*
2076 * Take an additional reference to the page or the VACB.
2077 */
2078 if (DirectMapped && !Context.Private)
2079 {
2080 if(!MiIsPageFromCache(MemoryArea, Context.Offset.QuadPart))
2081 {
2082 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
2083 KeBugCheck(MEMORY_MANAGEMENT);
2084 }
2085 }
2086 else
2087 {
2088 OldIrql = MiAcquirePfnLock();
2089 MmReferencePage(Page);
2090 MiReleasePfnLock(OldIrql);
2091 }
2092
2093 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
2094
2095 /* Since we passed in a surrogate, we'll get back the page entry
2096 * state in our context. This is intended to make intermediate
2097 * decrements of share count not release the wait entry.
2098 */
2099 Entry = Context.SectionEntry;
2100
2101 /*
2102 * If this wasn't a private page then we should have reduced the entry to
2103 * zero by deleting all the rmaps.
2104 */
2105 if (!Context.Private && Entry != 0)
2106 {
2107 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
2108 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2109 {
2110 KeBugCheckEx(MEMORY_MANAGEMENT, Entry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2111 }
2112 }
2113
2114 /*
2115 * If the page wasn't dirty then we can just free it as for a readonly page.
2116 * Since we unmapped all the mappings above we know it will not suddenly
2117 * become dirty.
2118 * If the page is from a pagefile section and has no swap entry,
2119 * we can't free the page at this point.
2120 */
2121 SwapEntry = MmGetSavedSwapEntryPage(Page);
2122 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
2123 {
2124 if (Context.Private)
2125 {
2126 DPRINT1("Found a %s private page (address %p) in a pagefile segment.\n",
2127 Context.WasDirty ? "dirty" : "clean", Address);
2128 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2129 }
2130 if (!Context.WasDirty && SwapEntry != 0)
2131 {
2132 MmSetSavedSwapEntryPage(Page, 0);
2133 MmLockSectionSegment(Context.Segment);
2134 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2135 MmUnlockSectionSegment(Context.Segment);
2136 MmReleasePageMemoryConsumer(MC_USER, Page);
2137 MiSetPageEvent(NULL, NULL);
2138 return(STATUS_SUCCESS);
2139 }
2140 }
2141 else if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2142 {
2143 if (Context.Private)
2144 {
2145 DPRINT1("Found a %s private page (address %p) in a shared section segment.\n",
2146 Context.WasDirty ? "dirty" : "clean", Address);
2147 KeBugCheckEx(MEMORY_MANAGEMENT, Page, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2148 }
2149 if (!Context.WasDirty || SwapEntry != 0)
2150 {
2151 MmSetSavedSwapEntryPage(Page, 0);
2152 if (SwapEntry != 0)
2153 {
2154 MmLockSectionSegment(Context.Segment);
2155 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2156 MmUnlockSectionSegment(Context.Segment);
2157 }
2158 MmReleasePageMemoryConsumer(MC_USER, Page);
2159 MiSetPageEvent(NULL, NULL);
2160 return(STATUS_SUCCESS);
2161 }
2162 }
2163 else if (!Context.Private && DirectMapped)
2164 {
2165 if (SwapEntry != 0)
2166 {
2167 DPRINT1("Found a swapentry for a non private and direct mapped page (address %p)\n",
2168 Address);
2169 KeBugCheckEx(MEMORY_MANAGEMENT, STATUS_UNSUCCESSFUL, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address);
2170 }
2171 #ifndef NEWCC
2172 Status = CcRosUnmapVacb(SharedCacheMap, FileOffset, FALSE);
2173 #else
2174 Status = STATUS_SUCCESS;
2175 #endif
2176 #ifndef NEWCC
2177 if (!NT_SUCCESS(Status))
2178 {
2179 DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
2180 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)SharedCacheMap, (ULONG_PTR)FileOffset, (ULONG_PTR)Address);
2181 }
2182 #endif
2183 MiSetPageEvent(NULL, NULL);
2184 return(STATUS_SUCCESS);
2185 }
2186 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
2187 {
2188 if (SwapEntry != 0)
2189 {
2190 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %p)\n",
2191 Address);
2192 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, Page, (ULONG_PTR)Process, (ULONG_PTR)Address);
2193 }
2194 MmReleasePageMemoryConsumer(MC_USER, Page);
2195 MiSetPageEvent(NULL, NULL);
2196 return(STATUS_SUCCESS);
2197 }
2198 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
2199 {
2200 DPRINT("Not dirty and private and not swapped (%p:%p)\n", Process, Address);
2201 MmSetSavedSwapEntryPage(Page, 0);
2202 MmLockAddressSpace(AddressSpace);
2203 Status = MmCreatePageFileMapping(Process,
2204 Address,
2205 SwapEntry);
2206 MmUnlockAddressSpace(AddressSpace);
2207 if (!NT_SUCCESS(Status))
2208 {
2209 DPRINT1("Status %x Swapping out %p:%p\n", Status, Process, Address);
2210 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2211 }
2212 MmReleasePageMemoryConsumer(MC_USER, Page);
2213 MiSetPageEvent(NULL, NULL);
2214 return(STATUS_SUCCESS);
2215 }
2216
2217 /*
2218 * If necessary, allocate an entry in the paging file for this page
2219 */
2220 if (SwapEntry == 0)
2221 {
2222 SwapEntry = MmAllocSwapPage();
2223 if (SwapEntry == 0)
2224 {
2225 MmShowOutOfSpaceMessagePagingFile();
2226 MmLockAddressSpace(AddressSpace);
2227 /*
2228 * For private pages restore the old mappings.
2229 */
2230 if (Context.Private)
2231 {
2232 Status = MmCreateVirtualMapping(Process,
2233 Address,
2234 MemoryArea->Protect,
2235 &Page,
2236 1);
2237 MmSetDirtyPage(Process, Address);
2238 MmInsertRmap(Page,
2239 Process,
2240 Address);
2241 }
2242 else
2243 {
2244 ULONG_PTR OldEntry;
2245
2246 MmLockSectionSegment(Context.Segment);
2247
2248 /*
2249 * For non-private pages if the page wasn't direct mapped then
2250 * set it back into the section segment entry so we don't loose
2251 * our copy. Otherwise it will be handled by the cache manager.
2252 */
2253 Status = MmCreateVirtualMapping(Process,
2254 Address,
2255 MemoryArea->Protect,
2256 &Page,
2257 1);
2258 MmSetDirtyPage(Process, Address);
2259 MmInsertRmap(Page,
2260 Process,
2261 Address);
2262 // If we got here, the previous entry should have been a wait
2263 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2264 OldEntry = MmGetPageEntrySectionSegment(Context.Segment, &Context.Offset);
2265 ASSERT(OldEntry == 0 || OldEntry == MAKE_SWAP_SSE(MM_WAIT_ENTRY));
2266 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2267 MmUnlockSectionSegment(Context.Segment);
2268 }
2269 MmUnlockAddressSpace(AddressSpace);
2270 MiSetPageEvent(NULL, NULL);
2271 return(STATUS_PAGEFILE_QUOTA);
2272 }
2273 }
2274
2275 /*
2276 * Write the page to the pagefile
2277 */
2278 Status = MmWriteToSwapPage(SwapEntry, Page);
2279 if (!NT_SUCCESS(Status))
2280 {
2281 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2282 Status);
2283 /*
2284 * As above: undo our actions.
2285 * FIXME: Also free the swap page.
2286 */
2287 MmLockAddressSpace(AddressSpace);
2288 if (Context.Private)
2289 {
2290 Status = MmCreateVirtualMapping(Process,
2291 Address,
2292 MemoryArea->Protect,
2293 &Page,
2294 1);
2295 MmSetDirtyPage(Process, Address);
2296 MmInsertRmap(Page,
2297 Process,
2298 Address);
2299 }
2300 else
2301 {
2302 MmLockSectionSegment(Context.Segment);
2303 Status = MmCreateVirtualMapping(Process,
2304 Address,
2305 MemoryArea->Protect,
2306 &Page,
2307 1);
2308 MmSetDirtyPage(Process, Address);
2309 MmInsertRmap(Page,
2310 Process,
2311 Address);
2312 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2313 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2314 MmUnlockSectionSegment(Context.Segment);
2315 }
2316 MmUnlockAddressSpace(AddressSpace);
2317 MiSetPageEvent(NULL, NULL);
2318 return(STATUS_UNSUCCESSFUL);
2319 }
2320
2321 /*
2322 * Otherwise we have succeeded.
2323 */
2324 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2325 MmSetSavedSwapEntryPage(Page, 0);
2326 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
2327 Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2328 {
2329 MmLockSectionSegment(Context.Segment);
2330 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2331 MmUnlockSectionSegment(Context.Segment);
2332 }
2333 else
2334 {
2335 MmReleasePageMemoryConsumer(MC_USER, Page);
2336 }
2337
2338 if (Context.Private)
2339 {
2340 MmLockAddressSpace(AddressSpace);
2341 MmLockSectionSegment(Context.Segment);
2342 Status = MmCreatePageFileMapping(Process,
2343 Address,
2344 SwapEntry);
2345 /* We had placed a wait entry upon entry ... replace it before leaving */
2346 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2347 MmUnlockSectionSegment(Context.Segment);
2348 MmUnlockAddressSpace(AddressSpace);
2349 if (!NT_SUCCESS(Status))
2350 {
2351 DPRINT1("Status %x Creating page file mapping for %p:%p\n", Status, Process, Address);
2352 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2353 }
2354 }
2355 else
2356 {
2357 MmLockAddressSpace(AddressSpace);
2358 MmLockSectionSegment(Context.Segment);
2359 Entry = MAKE_SWAP_SSE(SwapEntry);
2360 /* We had placed a wait entry upon entry ... replace it before leaving */
2361 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2362 MmUnlockSectionSegment(Context.Segment);
2363 MmUnlockAddressSpace(AddressSpace);
2364 }
2365
2366 MiSetPageEvent(NULL, NULL);
2367 return(STATUS_SUCCESS);
2368 }
2369
2370 NTSTATUS
2371 NTAPI
2372 MmWritePageSectionView(PMMSUPPORT AddressSpace,
2373 PMEMORY_AREA MemoryArea,
2374 PVOID Address,
2375 ULONG PageEntry)
2376 {
2377 LARGE_INTEGER Offset;
2378 PROS_SECTION_OBJECT Section;
2379 PMM_SECTION_SEGMENT Segment;
2380 PFN_NUMBER Page;
2381 SWAPENTRY SwapEntry;
2382 ULONG_PTR Entry;
2383 BOOLEAN Private;
2384 NTSTATUS Status;
2385 PFILE_OBJECT FileObject;
2386 #ifndef NEWCC
2387 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
2388 #endif
2389 BOOLEAN DirectMapped;
2390 BOOLEAN IsImageSection;
2391 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2392
2393 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2394
2395 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
2396 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2397
2398 /*
2399 * Get the segment and section.
2400 */
2401 Segment = MemoryArea->Data.SectionData.Segment;
2402 Section = MemoryArea->Data.SectionData.Section;
2403 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2404
2405 FileObject = Section->FileObject;
2406 DirectMapped = FALSE;
2407 if (FileObject != NULL &&
2408 !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2409 {
2410 #ifndef NEWCC
2411 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
2412 #endif
2413
2414 /*
2415 * If the file system is letting us go directly to the cache and the
2416 * memory area was mapped at an offset in the file which is page aligned
2417 * then note this is a direct mapped page.
2418 */
2419 if (((Offset.QuadPart + Segment->Image.FileOffset) % PAGE_SIZE) == 0 &&
2420 (Offset.QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
2421 {
2422 DirectMapped = TRUE;
2423 }
2424 }
2425
2426 /*
2427 * This should never happen since mappings of physical memory are never
2428 * placed in the rmap lists.
2429 */
2430 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2431 {
2432 DPRINT1("Trying to write back page from physical memory mapped at %p "
2433 "process %p\n", Address,
2434 Process ? Process->UniqueProcessId : 0);
2435 KeBugCheck(MEMORY_MANAGEMENT);
2436 }
2437
2438 /*
2439 * Get the section segment entry and the physical address.
2440 */
2441 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2442 if (!MmIsPagePresent(Process, Address))
2443 {
2444 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
2445 Process ? Process->UniqueProcessId : 0, Address);
2446 KeBugCheck(MEMORY_MANAGEMENT);
2447 }
2448 Page = MmGetPfnForProcess(Process, Address);
2449 SwapEntry = MmGetSavedSwapEntryPage(Page);
2450
2451 /*
2452 * Check for a private (COWed) page.
2453 */
2454 if (IS_SWAP_FROM_SSE(Entry) || PFN_FROM_SSE(Entry) != Page)
2455 {
2456 Private = TRUE;
2457 }
2458 else
2459 {
2460 Private = FALSE;
2461 }
2462
2463 /*
2464 * Speculatively set all mappings of the page to clean.
2465 */
2466 MmSetCleanAllRmaps(Page);
2467
2468 /*
2469 * If this page was direct mapped from the cache then the cache manager
2470 * will take care of writing it back to disk.
2471 */
2472 if (DirectMapped && !Private)
2473 {
2474 //LARGE_INTEGER SOffset;
2475 ASSERT(SwapEntry == 0);
2476 //SOffset.QuadPart = Offset.QuadPart + Segment->Image.FileOffset;
2477 #ifndef NEWCC
2478 CcRosMarkDirtyFile(SharedCacheMap, Offset.QuadPart);
2479 #endif
2480 MmLockSectionSegment(Segment);
2481 MmSetPageEntrySectionSegment(Segment, &Offset, PageEntry);
2482 MmUnlockSectionSegment(Segment);
2483 MiSetPageEvent(NULL, NULL);
2484 return(STATUS_SUCCESS);
2485 }
2486
2487 /*
2488 * If necessary, allocate an entry in the paging file for this page
2489 */
2490 if (SwapEntry == 0)
2491 {
2492 SwapEntry = MmAllocSwapPage();
2493 if (SwapEntry == 0)
2494 {
2495 MmSetDirtyAllRmaps(Page);
2496 MiSetPageEvent(NULL, NULL);
2497 return(STATUS_PAGEFILE_QUOTA);
2498 }
2499 MmSetSavedSwapEntryPage(Page, SwapEntry);
2500 }
2501
2502 /*
2503 * Write the page to the pagefile
2504 */
2505 Status = MmWriteToSwapPage(SwapEntry, Page);
2506 if (!NT_SUCCESS(Status))
2507 {
2508 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2509 Status);
2510 MmSetDirtyAllRmaps(Page);
2511 MiSetPageEvent(NULL, NULL);
2512 return(STATUS_UNSUCCESSFUL);
2513 }
2514
2515 /*
2516 * Otherwise we have succeeded.
2517 */
2518 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2519 MiSetPageEvent(NULL, NULL);
2520 return(STATUS_SUCCESS);
2521 }
2522
2523 NTSTATUS
2524 NTAPI
2525 MmProtectSectionView(PMMSUPPORT AddressSpace,
2526 PMEMORY_AREA MemoryArea,
2527 PVOID BaseAddress,
2528 SIZE_T Length,
2529 ULONG Protect,
2530 PULONG OldProtect)
2531 {
2532 PMM_REGION Region;
2533 NTSTATUS Status;
2534 ULONG_PTR MaxLength;
2535
2536 MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2537 if (Length > MaxLength)
2538 Length = (ULONG)MaxLength;
2539
2540 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2541 &MemoryArea->Data.SectionData.RegionListHead,
2542 BaseAddress, NULL);
2543 ASSERT(Region != NULL);
2544
2545 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2546 Region->Protect != Protect)
2547 {
2548 return STATUS_INVALID_PAGE_PROTECTION;
2549 }
2550
2551 *OldProtect = Region->Protect;
2552 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2553 &MemoryArea->Data.SectionData.RegionListHead,
2554 BaseAddress, Length, Region->Type, Protect,
2555 MmAlterViewAttributes);
2556
2557 return(Status);
2558 }
2559
2560 NTSTATUS NTAPI
2561 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2562 PVOID Address,
2563 PMEMORY_BASIC_INFORMATION Info,
2564 PSIZE_T ResultLength)
2565 {
2566 PMM_REGION Region;
2567 PVOID RegionBaseAddress;
2568 PROS_SECTION_OBJECT Section;
2569 PMM_SECTION_SEGMENT Segment;
2570
2571 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2572 &MemoryArea->Data.SectionData.RegionListHead,
2573 Address, &RegionBaseAddress);
2574 if (Region == NULL)
2575 {
2576 return STATUS_UNSUCCESSFUL;
2577 }
2578
2579 Section = MemoryArea->Data.SectionData.Section;
2580 if (Section->AllocationAttributes & SEC_IMAGE)
2581 {
2582 Segment = MemoryArea->Data.SectionData.Segment;
2583 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2584 Info->Type = MEM_IMAGE;
2585 }
2586 else
2587 {
2588 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2589 Info->Type = MEM_MAPPED;
2590 }
2591 Info->BaseAddress = RegionBaseAddress;
2592 Info->AllocationProtect = MemoryArea->Protect;
2593 Info->RegionSize = Region->Length;
2594 Info->State = MEM_COMMIT;
2595 Info->Protect = Region->Protect;
2596
2597 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2598 return(STATUS_SUCCESS);
2599 }
2600
2601 VOID
2602 NTAPI
2603 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2604 {
2605 ULONG Length;
2606 LARGE_INTEGER Offset;
2607 ULONG_PTR Entry;
2608 SWAPENTRY SavedSwapEntry;
2609 PFN_NUMBER Page;
2610
2611 Page = 0;
2612
2613 MmLockSectionSegment(Segment);
2614
2615 Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
2616 for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
2617 {
2618 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2619 if (Entry)
2620 {
2621 MmSetPageEntrySectionSegment(Segment, &Offset, 0);
2622 if (IS_SWAP_FROM_SSE(Entry))
2623 {
2624 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2625 }
2626 else
2627 {
2628 Page = PFN_FROM_SSE(Entry);
2629 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2630 if (SavedSwapEntry != 0)
2631 {
2632 MmSetSavedSwapEntryPage(Page, 0);
2633 MmFreeSwapPage(SavedSwapEntry);
2634 }
2635 MmReleasePageMemoryConsumer(MC_USER, Page);
2636 }
2637 }
2638 }
2639
2640 MmUnlockSectionSegment(Segment);
2641 }
2642
2643 VOID NTAPI
2644 MmpDeleteSection(PVOID ObjectBody)
2645 {
2646 PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
2647
2648 /* Check if it's an ARM3, or ReactOS section */
2649 if (!MiIsRosSectionObject(Section))
2650 {
2651 MiDeleteARM3Section(ObjectBody);
2652 return;
2653 }
2654
2655 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2656 if (Section->AllocationAttributes & SEC_IMAGE)
2657 {
2658 ULONG i;
2659 ULONG NrSegments;
2660 ULONG RefCount;
2661 PMM_SECTION_SEGMENT SectionSegments;
2662
2663 /*
2664 * NOTE: Section->ImageSection can be NULL for short time
2665 * during the section creating. If we fail for some reason
2666 * until the image section is properly initialized we shouldn't
2667 * process further here.
2668 */
2669 if (Section->ImageSection == NULL)
2670 return;
2671
2672 SectionSegments = Section->ImageSection->Segments;
2673 NrSegments = Section->ImageSection->NrSegments;
2674
2675 for (i = 0; i < NrSegments; i++)
2676 {
2677 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2678 {
2679 MmLockSectionSegment(&SectionSegments[i]);
2680 }
2681 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2682 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2683 {
2684 MmUnlockSectionSegment(&SectionSegments[i]);
2685 if (RefCount == 0)
2686 {
2687 MmpFreePageFileSegment(&SectionSegments[i]);
2688 }
2689 }
2690 }
2691 }
2692 #ifdef NEWCC
2693 else if (Section->Segment && Section->Segment->Flags & MM_DATAFILE_SEGMENT)
2694 {
2695 ULONG RefCount = 0;
2696 PMM_SECTION_SEGMENT Segment = Section->Segment;
2697
2698 if (Segment &&
2699 (RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
2700 {
2701 DPRINT("Freeing section segment\n");
2702 Section->Segment = NULL;
2703 MmFinalizeSegment(Segment);
2704 }
2705 else
2706 {
2707 DPRINT("RefCount %d\n", RefCount);
2708 }
2709 }
2710 #endif
2711 else
2712 {
2713 /*
2714 * NOTE: Section->Segment can be NULL for short time
2715 * during the section creating.
2716 */
2717 if (Section->Segment == NULL)
2718 return;
2719
2720 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2721 {
2722 MmpFreePageFileSegment(Section->Segment);
2723 MmFreePageTablesSectionSegment(Section->Segment, NULL);
2724 ExFreePool(Section->Segment);
2725 Section->Segment = NULL;
2726 }
2727 else
2728 {
2729 (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2730 }
2731 }
2732 if (Section->FileObject != NULL)
2733 {
2734 #ifndef NEWCC
2735 CcRosDereferenceCache(Section->FileObject);
2736 #endif
2737 ObDereferenceObject(Section->FileObject);
2738 Section->FileObject = NULL;
2739 }
2740 }
2741
2742 VOID NTAPI
2743 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2744 IN PVOID Object,
2745 IN ACCESS_MASK GrantedAccess,
2746 IN ULONG ProcessHandleCount,
2747 IN ULONG SystemHandleCount)
2748 {
2749 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2750 }
2751
2752 NTSTATUS
2753 INIT_FUNCTION
2754 NTAPI
2755 MmCreatePhysicalMemorySection(VOID)
2756 {
2757 PROS_SECTION_OBJECT PhysSection;
2758 NTSTATUS Status;
2759 OBJECT_ATTRIBUTES Obj;
2760 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2761 LARGE_INTEGER SectionSize;
2762 HANDLE Handle;
2763
2764 /*
2765 * Create the section mapping physical memory
2766 */
2767 SectionSize.QuadPart = 0xFFFFFFFF;
2768 InitializeObjectAttributes(&Obj,
2769 &Name,
2770 OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
2771 NULL,
2772 NULL);
2773 Status = MmCreateSection((PVOID)&PhysSection,
2774 SECTION_ALL_ACCESS,
2775 &Obj,
2776 &SectionSize,
2777 PAGE_EXECUTE_READWRITE,
2778 SEC_PHYSICALMEMORY,
2779 NULL,
2780 NULL);
2781 if (!NT_SUCCESS(Status))
2782 {
2783 DPRINT1("Failed to create PhysicalMemory section\n");
2784 KeBugCheck(MEMORY_MANAGEMENT);
2785 }
2786 Status = ObInsertObject(PhysSection,
2787 NULL,
2788 SECTION_ALL_ACCESS,
2789 0,
2790 NULL,
2791 &Handle);
2792 if (!NT_SUCCESS(Status))
2793 {
2794 ObDereferenceObject(PhysSection);
2795 }
2796 ObCloseHandle(Handle, KernelMode);
2797 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2798 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2799
2800 return(STATUS_SUCCESS);
2801 }
2802
2803 NTSTATUS
2804 INIT_FUNCTION
2805 NTAPI
2806 MmInitSectionImplementation(VOID)
2807 {
2808 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2809 UNICODE_STRING Name;
2810
2811 DPRINT("Creating Section Object Type\n");
2812
2813 /* Initialize the section based root */
2814 ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2815 MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2816
2817 /* Initialize the Section object type */
2818 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2819 RtlInitUnicodeString(&Name, L"Section");
2820 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2821 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT);
2822 ObjectTypeInitializer.PoolType = PagedPool;
2823 ObjectTypeInitializer.UseDefaultObject = TRUE;
2824 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2825 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2826 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2827 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2828 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2829 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2830
2831 MmCreatePhysicalMemorySection();
2832
2833 return(STATUS_SUCCESS);
2834 }
2835
2836 NTSTATUS
2837 NTAPI
2838 MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
2839 ACCESS_MASK DesiredAccess,
2840 POBJECT_ATTRIBUTES ObjectAttributes,
2841 PLARGE_INTEGER UMaximumSize,
2842 ULONG SectionPageProtection,
2843 ULONG AllocationAttributes)
2844 /*
2845 * Create a section which is backed by the pagefile
2846 */
2847 {
2848 LARGE_INTEGER MaximumSize;
2849 PROS_SECTION_OBJECT Section;
2850 PMM_SECTION_SEGMENT Segment;
2851 NTSTATUS Status;
2852
2853 if (UMaximumSize == NULL)
2854 {
2855 DPRINT1("MmCreatePageFileSection: (UMaximumSize == NULL)\n");
2856 return(STATUS_INVALID_PARAMETER);
2857 }
2858 MaximumSize = *UMaximumSize;
2859
2860 /*
2861 * Create the section
2862 */
2863 Status = ObCreateObject(ExGetPreviousMode(),
2864 MmSectionObjectType,
2865 ObjectAttributes,
2866 ExGetPreviousMode(),
2867 NULL,
2868 sizeof(ROS_SECTION_OBJECT),
2869 0,
2870 0,
2871 (PVOID*)(PVOID)&Section);
2872 if (!NT_SUCCESS(Status))
2873 {
2874 DPRINT1("MmCreatePageFileSection: failed to create object (0x%lx)\n", Status);
2875 return(Status);
2876 }
2877
2878 /*
2879 * Initialize it
2880 */
2881 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2882 Section->Type = 'SC';
2883 Section->Size = 'TN';
2884 Section->SectionPageProtection = SectionPageProtection;
2885 Section->AllocationAttributes = AllocationAttributes;
2886 Section->MaximumSize = MaximumSize;
2887 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2888 TAG_MM_SECTION_SEGMENT);
2889 if (Segment == NULL)
2890 {
2891 ObDereferenceObject(Section);
2892 return(STATUS_NO_MEMORY);
2893 }
2894 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2895 Section->Segment = Segment;
2896 Segment->ReferenceCount = 1;
2897 ExInitializeFastMutex(&Segment->Lock);
2898 Segment->Image.FileOffset = 0;
2899 Segment->Protection = SectionPageProtection;
2900 Segment->RawLength.QuadPart = MaximumSize.u.LowPart;
2901 Segment->Length.QuadPart = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2902 Segment->Flags = MM_PAGEFILE_SEGMENT;
2903 Segment->WriteCopy = FALSE;
2904 Segment->Image.VirtualAddress = 0;
2905 Segment->Image.Characteristics = 0;
2906 *SectionObject = Section;
2907 MiInitializeSectionPageTable(Segment);
2908 return(STATUS_SUCCESS);
2909 }
2910
2911 NTSTATUS
2912 NTAPI
2913 MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject,
2914 ACCESS_MASK DesiredAccess,
2915 POBJECT_ATTRIBUTES ObjectAttributes,
2916 PLARGE_INTEGER UMaximumSize,
2917 ULONG SectionPageProtection,
2918 ULONG AllocationAttributes,
2919 PFILE_OBJECT FileObject)
2920 /*
2921 * Create a section backed by a data file
2922 */
2923 {
2924 PROS_SECTION_OBJECT Section;
2925 NTSTATUS Status;
2926 LARGE_INTEGER MaximumSize;
2927 PMM_SECTION_SEGMENT Segment;
2928 FILE_STANDARD_INFORMATION FileInfo;
2929 ULONG Length;
2930
2931 /*
2932 * Create the section
2933 */
2934 Status = ObCreateObject(ExGetPreviousMode(),
2935 MmSectionObjectType,
2936 ObjectAttributes,
2937 ExGetPreviousMode(),
2938 NULL,
2939 sizeof(ROS_SECTION_OBJECT),
2940 0,
2941 0,
2942 (PVOID*)&Section);
2943 if (!NT_SUCCESS(Status))
2944 {
2945 ObDereferenceObject(FileObject);
2946 return(Status);
2947 }
2948 /*
2949 * Initialize it
2950 */
2951 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2952 Section->Type = 'SC';
2953 Section->Size = 'TN';
2954 Section->SectionPageProtection = SectionPageProtection;
2955 Section->AllocationAttributes = AllocationAttributes;
2956
2957 /*
2958 * FIXME: This is propably not entirely correct. We can't look into
2959 * the standard FCB header because it might not be initialized yet
2960 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2961 * standard file information is filled on first request).
2962 */
2963 Status = IoQueryFileInformation(FileObject,
2964 FileStandardInformation,
2965 sizeof(FILE_STANDARD_INFORMATION),
2966 &FileInfo,
2967 &Length);
2968 if (!NT_SUCCESS(Status))
2969 {
2970 ObDereferenceObject(Section);
2971 ObDereferenceObject(FileObject);
2972 return Status;
2973 }
2974
2975 /*
2976 * FIXME: Revise this once a locking order for file size changes is
2977 * decided
2978 */
2979 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2980 {
2981 MaximumSize = *UMaximumSize;
2982 }
2983 else
2984 {
2985 MaximumSize = FileInfo.EndOfFile;
2986 /* Mapping zero-sized files isn't allowed. */
2987 if (MaximumSize.QuadPart == 0)
2988 {
2989 ObDereferenceObject(Section);
2990 ObDereferenceObject(FileObject);
2991 return STATUS_MAPPED_FILE_SIZE_ZERO;
2992 }
2993 }
2994
2995 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
2996 {
2997 Status = IoSetInformation(FileObject,
2998 FileEndOfFileInformation,
2999 sizeof(LARGE_INTEGER),
3000 &MaximumSize);
3001 if (!NT_SUCCESS(Status))
3002 {
3003 ObDereferenceObject(Section);
3004 ObDereferenceObject(FileObject);
3005 return(STATUS_SECTION_NOT_EXTENDED);
3006 }
3007 }
3008
3009 if (FileObject->SectionObjectPointer == NULL ||
3010 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3011 {
3012 ObDereferenceObject(Section);
3013 ObDereferenceObject(FileObject);
3014 return STATUS_INVALID_FILE_FOR_SECTION;
3015 }
3016
3017 /*
3018 * Lock the file
3019 */
3020 Status = MmspWaitForFileLock(FileObject);
3021 if (Status != STATUS_SUCCESS)
3022 {
3023 ObDereferenceObject(Section);
3024 ObDereferenceObject(FileObject);
3025 return(Status);
3026 }
3027
3028 /*
3029 * If this file hasn't been mapped as a data file before then allocate a
3030 * section segment to describe the data file mapping
3031 */
3032 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
3033 {
3034 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
3035 TAG_MM_SECTION_SEGMENT);
3036 if (Segment == NULL)
3037 {
3038 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3039 ObDereferenceObject(Section);
3040 ObDereferenceObject(FileObject);
3041 return(STATUS_NO_MEMORY);
3042 }
3043 Section->Segment = Segment;
3044 Segment->ReferenceCount = 1;
3045 ExInitializeFastMutex(&Segment->Lock);
3046 /*
3047 * Set the lock before assigning the segment to the file object
3048 */
3049 ExAcquireFastMutex(&Segment->Lock);
3050 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
3051
3052 Segment->Image.FileOffset = 0;
3053 Segment->Protection = SectionPageProtection;
3054 Segment->Flags = MM_DATAFILE_SEGMENT;
3055 Segment->Image.Characteristics = 0;
3056 Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
3057 if (AllocationAttributes & SEC_RESERVE)
3058 {
3059 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
3060 }
3061 else
3062 {
3063 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3064 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3065 }
3066 Segment->Image.VirtualAddress = 0;
3067 Segment->Locked = TRUE;
3068 MiInitializeSectionPageTable(Segment);
3069 }
3070 else
3071 {
3072 /*
3073 * If the file is already mapped as a data file then we may need
3074 * to extend it
3075 */
3076 Segment =
3077 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
3078 DataSectionObject;
3079 Section->Segment = Segment;
3080 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
3081 MmLockSectionSegment(Segment);
3082
3083 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
3084 !(AllocationAttributes & SEC_RESERVE))
3085 {
3086 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3087 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3088 }
3089 }
3090 MmUnlockSectionSegment(Segment);
3091 Section->FileObject = FileObject;
3092 Section->MaximumSize = MaximumSize;
3093 #ifndef NEWCC
3094 CcRosReferenceCache(FileObject);
3095 #endif
3096 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3097 *SectionObject = Section;
3098 return(STATUS_SUCCESS);
3099 }
3100
3101 /*
3102 TODO: not that great (declaring loaders statically, having to declare all of
3103 them, having to keep them extern, etc.), will fix in the future
3104 */
3105 extern NTSTATUS NTAPI PeFmtCreateSection
3106 (
3107 IN CONST VOID * FileHeader,
3108 IN SIZE_T FileHeaderSize,
3109 IN PVOID File,
3110 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3111 OUT PULONG Flags,
3112 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3113 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3114 );
3115
3116 extern NTSTATUS NTAPI ElfFmtCreateSection
3117 (
3118 IN CONST VOID * FileHeader,
3119 IN SIZE_T FileHeaderSize,
3120 IN PVOID File,
3121 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3122 OUT PULONG Flags,
3123 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3124 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3125 );
3126
3127 static PEXEFMT_LOADER ExeFmtpLoaders[] =
3128 {
3129 PeFmtCreateSection,
3130 #ifdef __ELF
3131 ElfFmtCreateSection
3132 #endif
3133 };
3134
3135 static
3136 PMM_SECTION_SEGMENT
3137 NTAPI
3138 ExeFmtpAllocateSegments(IN ULONG NrSegments)
3139 {
3140 SIZE_T SizeOfSegments;
3141 PMM_SECTION_SEGMENT Segments;
3142
3143 /* TODO: check for integer overflow */
3144 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
3145
3146 Segments = ExAllocatePoolWithTag(NonPagedPool,
3147 SizeOfSegments,
3148 TAG_MM_SECTION_SEGMENT);
3149
3150 if(Segments)
3151 RtlZeroMemory(Segments, SizeOfSegments);
3152
3153 return Segments;
3154 }
3155
3156 static
3157 NTSTATUS
3158 NTAPI
3159 ExeFmtpReadFile(IN PVOID File,
3160 IN PLARGE_INTEGER Offset,
3161 IN ULONG Length,
3162 OUT PVOID * Data,
3163 OUT PVOID * AllocBase,
3164 OUT PULONG ReadSize)
3165 {
3166 NTSTATUS Status;
3167 LARGE_INTEGER FileOffset;
3168 ULONG AdjustOffset;
3169 ULONG OffsetAdjustment;
3170 ULONG BufferSize;
3171 ULONG UsedSize;
3172 PVOID Buffer;
3173 PFILE_OBJECT FileObject = File;
3174 IO_STATUS_BLOCK Iosb;
3175
3176 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
3177
3178 if(Length == 0)
3179 {
3180 KeBugCheck(MEMORY_MANAGEMENT);
3181 }
3182
3183 FileOffset = *Offset;
3184
3185 /* Negative/special offset: it cannot be used in this context */
3186 if(FileOffset.u.HighPart < 0)
3187 {
3188 KeBugCheck(MEMORY_MANAGEMENT);
3189 }
3190
3191 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
3192 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
3193 FileOffset.u.LowPart = AdjustOffset;
3194
3195 BufferSize = Length + OffsetAdjustment;
3196 BufferSize = PAGE_ROUND_UP(BufferSize);
3197
3198 /* Flush data since we're about to perform a non-cached read */
3199 CcFlushCache(FileObject->SectionObjectPointer,
3200 &FileOffset,
3201 BufferSize,
3202 &Iosb);
3203
3204 /*
3205 * It's ok to use paged pool, because this is a temporary buffer only used in
3206 * the loading of executables. The assumption is that MmCreateSection is
3207 * always called at low IRQLs and that these buffers don't survive a brief
3208 * initialization phase
3209 */
3210 Buffer = ExAllocatePoolWithTag(PagedPool,
3211 BufferSize,
3212 'rXmM');
3213 if (!Buffer)
3214 {
3215 return STATUS_INSUFFICIENT_RESOURCES;
3216 }
3217
3218 UsedSize = 0;
3219
3220 Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
3221
3222 UsedSize = (ULONG)Iosb.Information;
3223
3224 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
3225 {
3226 Status = STATUS_IN_PAGE_ERROR;
3227 ASSERT(!NT_SUCCESS(Status));
3228 }
3229
3230 if(NT_SUCCESS(Status))
3231 {
3232 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
3233 *AllocBase = Buffer;
3234 *ReadSize = UsedSize - OffsetAdjustment;
3235 }
3236 else
3237 {
3238 ExFreePoolWithTag(Buffer, 'rXmM');
3239 }
3240
3241 return Status;
3242 }
3243
3244 #ifdef NASSERT
3245 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
3246 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
3247 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
3248 #else
3249 static
3250 VOID
3251 NTAPI
3252 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3253 {
3254 ULONG i;
3255
3256 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
3257 {
3258 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3259 ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
3260 }
3261 }
3262
3263 static
3264 VOID
3265 NTAPI
3266 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3267 {
3268 ULONG i;
3269
3270 MmspAssertSegmentsSorted(ImageSectionObject);
3271
3272 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3273 {
3274 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
3275
3276 if(i > 0)
3277 {
3278 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3279 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
3280 ImageSectionObject->Segments[i - 1].Length.QuadPart));
3281 }
3282 }
3283 }
3284
3285 static
3286 VOID
3287 NTAPI
3288 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3289 {
3290 ULONG i;
3291
3292 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3293 {
3294 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
3295 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
3296 }
3297 }
3298 #endif
3299
3300 static
3301 int
3302 __cdecl
3303 MmspCompareSegments(const void * x,
3304 const void * y)
3305 {
3306 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
3307 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
3308
3309 return
3310 (Segment1->Image.VirtualAddress - Segment2->Image.VirtualAddress) >>
3311 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
3312 }
3313
3314 /*
3315 * Ensures an image section's segments are sorted in memory
3316 */
3317 static
3318 VOID
3319 NTAPI
3320 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3321 IN ULONG Flags)
3322 {
3323 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
3324 {
3325 MmspAssertSegmentsSorted(ImageSectionObject);
3326 }
3327 else
3328 {
3329 qsort(ImageSectionObject->Segments,
3330 ImageSectionObject->NrSegments,
3331 sizeof(ImageSectionObject->Segments[0]),
3332 MmspCompareSegments);
3333 }
3334 }
3335
3336
3337 /*
3338 * Ensures an image section's segments don't overlap in memory and don't have
3339 * gaps and don't have a null size. We let them map to overlapping file regions,
3340 * though - that's not necessarily an error
3341 */
3342 static
3343 BOOLEAN
3344 NTAPI
3345 MmspCheckSegmentBounds
3346 (
3347 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3348 IN ULONG Flags
3349 )
3350 {
3351 ULONG i;
3352
3353 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
3354 {
3355 MmspAssertSegmentsNoOverlap(ImageSectionObject);
3356 return TRUE;
3357 }
3358
3359 ASSERT(ImageSectionObject->NrSegments >= 1);
3360
3361 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3362 {
3363 if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
3364 {
3365 return FALSE;
3366 }
3367
3368 if(i > 0)
3369 {
3370 /*
3371 * TODO: relax the limitation on gaps. F