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