- Missed these. Goodbye NT_ASSERT.
[reactos.git] / reactos / 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 static const INFORMATION_CLASS_INFO ExSectionInfoClass[] =
175 {
176 ICI_SQ_SAME( sizeof(SECTION_BASIC_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionBasicInformation */
177 ICI_SQ_SAME( sizeof(SECTION_IMAGE_INFORMATION), sizeof(ULONG), ICIF_QUERY ), /* SectionImageInformation */
178 };
179
180 /* FUNCTIONS *****************************************************************/
181
182
183 /*
184 References:
185 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
186 File Format Specification", revision 6.0 (February 1999)
187 */
188 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
189 IN SIZE_T FileHeaderSize,
190 IN PVOID File,
191 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
192 OUT PULONG Flags,
193 IN PEXEFMT_CB_READ_FILE ReadFileCb,
194 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
195 {
196 NTSTATUS nStatus;
197 ULONG cbFileHeaderOffsetSize = 0;
198 ULONG cbSectionHeadersOffset = 0;
199 ULONG cbSectionHeadersSize;
200 ULONG cbSectionHeadersOffsetSize = 0;
201 ULONG cbOptHeaderSize;
202 ULONG cbHeadersSize = 0;
203 ULONG nSectionAlignment;
204 ULONG nFileAlignment;
205 ULONG_PTR ImageBase;
206 const IMAGE_DOS_HEADER * pidhDosHeader;
207 const IMAGE_NT_HEADERS32 * pinhNtHeader;
208 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
209 const IMAGE_SECTION_HEADER * pishSectionHeaders;
210 PMM_SECTION_SEGMENT pssSegments;
211 LARGE_INTEGER lnOffset;
212 PVOID pBuffer;
213 SIZE_T nPrevVirtualEndOfSegment = 0;
214 ULONG nFileSizeOfHeaders = 0;
215 ULONG i;
216
217 ASSERT(FileHeader);
218 ASSERT(FileHeaderSize > 0);
219 ASSERT(File);
220 ASSERT(ImageSectionObject);
221 ASSERT(ReadFileCb);
222 ASSERT(AllocateSegmentsCb);
223
224 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
225
226 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
227
228 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
229
230 pBuffer = NULL;
231 pidhDosHeader = FileHeader;
232
233 /* DOS HEADER */
234 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
235
236 /* image too small to be an MZ executable */
237 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
238 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
239
240 /* no MZ signature */
241 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
242 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
243
244 /* NT HEADER */
245 nStatus = STATUS_INVALID_IMAGE_PROTECT;
246
247 /* not a Windows executable */
248 if(pidhDosHeader->e_lfanew <= 0)
249 DIE(("Not a Windows executable, e_lfanew is %d\n", pidhDosHeader->e_lfanew));
250
251 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
252 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
253
254 if(FileHeaderSize < cbFileHeaderOffsetSize)
255 pinhNtHeader = NULL;
256 else
257 {
258 /*
259 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
260 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
261 */
262 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
263 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
264 }
265
266 /*
267 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
268 * need to read the header from the file
269 */
270 if(FileHeaderSize < cbFileHeaderOffsetSize ||
271 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
272 {
273 ULONG cbNtHeaderSize;
274 ULONG cbReadSize;
275 PVOID pData;
276
277 l_ReadHeaderFromFile:
278 cbNtHeaderSize = 0;
279 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
280
281 /* read the header from the file */
282 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
283
284 if(!NT_SUCCESS(nStatus))
285 {
286 NTSTATUS ReturnedStatus = nStatus;
287
288 /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
289 if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
290
291 DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
292 }
293
294 ASSERT(pData);
295 ASSERT(pBuffer);
296 ASSERT(cbReadSize > 0);
297
298 nStatus = STATUS_INVALID_IMAGE_FORMAT;
299
300 /* the buffer doesn't contain the file header */
301 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
302 DIE(("The file doesn't contain the PE file header\n"));
303
304 pinhNtHeader = pData;
305
306 /* object still not aligned: copy it to the beginning of the buffer */
307 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
308 {
309 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
310 RtlMoveMemory(pBuffer, pData, cbReadSize);
311 pinhNtHeader = pBuffer;
312 }
313
314 /* invalid NT header */
315 nStatus = STATUS_INVALID_IMAGE_PROTECT;
316
317 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
318 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
319
320 nStatus = STATUS_INVALID_IMAGE_FORMAT;
321
322 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
323 DIE(("The full NT header is too large\n"));
324
325 /* the buffer doesn't contain the whole NT header */
326 if(cbReadSize < cbNtHeaderSize)
327 DIE(("The file doesn't contain the full NT header\n"));
328 }
329 else
330 {
331 ULONG cbOptHeaderOffsetSize = 0;
332
333 nStatus = STATUS_INVALID_IMAGE_PROTECT;
334
335 /* don't trust an invalid NT header */
336 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
337 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
338
339 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
340 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
341
342 nStatus = STATUS_INVALID_IMAGE_FORMAT;
343
344 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
345 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
346
347 /* the buffer doesn't contain the whole NT header: read it from the file */
348 if(cbOptHeaderOffsetSize > FileHeaderSize)
349 goto l_ReadHeaderFromFile;
350 }
351
352 /* read information from the NT header */
353 piohOptHeader = &pinhNtHeader->OptionalHeader;
354 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
355
356 nStatus = STATUS_INVALID_IMAGE_FORMAT;
357
358 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
359 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
360
361 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
362
363 switch(piohOptHeader->Magic)
364 {
365 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
366 #ifdef _WIN64
367 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
368 #endif // _WIN64
369 break;
370
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 = IMAGE_SCN_CNT_INITIALIZED_DATA;
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 pssSegments[i].Length.LowPart = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
764 /* FIXME: always false */
765 if (pssSegments[i].Length.QuadPart < pssSegments[i].Length.QuadPart)
766 DIE(("Cannot align the virtual size of section %u\n", i));
767
768 if(pssSegments[i].Length.QuadPart == 0)
769 DIE(("Virtual size of section %u is null\n", i));
770
771 pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
772 pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
773
774 /* ensure the memory image is no larger than 4GB */
775 nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
776 if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
777 DIE(("The image is too large\n"));
778 }
779
780 if(nSectionAlignment >= PAGE_SIZE)
781 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
782
783 /* Success */
784 nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
785
786 l_Return:
787 if(pBuffer)
788 ExFreePool(pBuffer);
789
790 return nStatus;
791 }
792
793 /*
794 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
795 * ARGUMENTS: PFILE_OBJECT to wait for.
796 * RETURNS: Status of the wait.
797 */
798 NTSTATUS
799 MmspWaitForFileLock(PFILE_OBJECT File)
800 {
801 return STATUS_SUCCESS;
802 //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
803 }
804
805 VOID
806 NTAPI
807 MmFreeSectionSegments(PFILE_OBJECT FileObject)
808 {
809 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
810 {
811 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
812 PMM_SECTION_SEGMENT SectionSegments;
813 ULONG NrSegments;
814 ULONG i;
815
816 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
817 NrSegments = ImageSectionObject->NrSegments;
818 SectionSegments = ImageSectionObject->Segments;
819 for (i = 0; i < NrSegments; i++)
820 {
821 if (SectionSegments[i].ReferenceCount != 0)
822 {
823 DPRINT1("Image segment %lu still referenced (was %lu)\n", i,
824 SectionSegments[i].ReferenceCount);
825 KeBugCheck(MEMORY_MANAGEMENT);
826 }
827 MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
828 }
829 ExFreePool(ImageSectionObject->Segments);
830 ExFreePool(ImageSectionObject);
831 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
832 }
833 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
834 {
835 PMM_SECTION_SEGMENT Segment;
836
837 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
838 DataSectionObject;
839
840 if (Segment->ReferenceCount != 0)
841 {
842 DPRINT1("Data segment still referenced\n");
843 KeBugCheck(MEMORY_MANAGEMENT);
844 }
845 MmFreePageTablesSectionSegment(Segment, NULL);
846 ExFreePool(Segment);
847 FileObject->SectionObjectPointer->DataSectionObject = NULL;
848 }
849 }
850
851 VOID
852 NTAPI
853 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
854 PLARGE_INTEGER Offset)
855 {
856 ULONG_PTR Entry;
857
858 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
859 if (Entry == 0)
860 {
861 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
862 KeBugCheck(MEMORY_MANAGEMENT);
863 }
864 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
865 {
866 DPRINT1("Maximum share count reached\n");
867 KeBugCheck(MEMORY_MANAGEMENT);
868 }
869 if (IS_SWAP_FROM_SSE(Entry))
870 {
871 KeBugCheck(MEMORY_MANAGEMENT);
872 }
873 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
874 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
875 }
876
877 BOOLEAN
878 NTAPI
879 MmUnsharePageEntrySectionSegment(PROS_SECTION_OBJECT Section,
880 PMM_SECTION_SEGMENT Segment,
881 PLARGE_INTEGER Offset,
882 BOOLEAN Dirty,
883 BOOLEAN PageOut,
884 ULONG_PTR *InEntry)
885 {
886 ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
887 BOOLEAN IsDirectMapped = FALSE;
888
889 if (Entry == 0)
890 {
891 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
892 KeBugCheck(MEMORY_MANAGEMENT);
893 }
894 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
895 {
896 DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
897 KeBugCheck(MEMORY_MANAGEMENT);
898 }
899 if (IS_SWAP_FROM_SSE(Entry))
900 {
901 KeBugCheck(MEMORY_MANAGEMENT);
902 }
903 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
904 /*
905 * If we reducing the share count of this entry to zero then set the entry
906 * to zero and tell the cache the page is no longer mapped.
907 */
908 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
909 {
910 PFILE_OBJECT FileObject;
911 SWAPENTRY SavedSwapEntry;
912 PFN_NUMBER Page;
913 #ifndef NEWCC
914 PROS_SHARED_CACHE_MAP SharedCacheMap;
915 BOOLEAN IsImageSection;
916 LARGE_INTEGER FileOffset;
917
918 FileOffset.QuadPart = Offset->QuadPart + Segment->Image.FileOffset;
919 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
920 #endif
921
922 Page = PFN_FROM_SSE(Entry);
923 FileObject = Section->FileObject;
924 if (FileObject != NULL &&
925 !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
926 {
927
928 #ifndef NEWCC
929 if ((FileOffset.QuadPart % PAGE_SIZE) == 0 &&
930 (Offset->QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
931 {
932 NTSTATUS Status;
933 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
934 IsDirectMapped = TRUE;
935 #ifndef NEWCC
936 Status = CcRosUnmapVacb(SharedCacheMap, FileOffset.QuadPart, Dirty);
937 #else
938 Status = STATUS_SUCCESS;
939 #endif
940 if (!NT_SUCCESS(Status))
941 {
942 DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
943 KeBugCheck(MEMORY_MANAGEMENT);
944 }
945 }
946 #endif
947 }
948
949 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
950 if (SavedSwapEntry == 0)
951 {
952 if (!PageOut &&
953 ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
954 (Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)))
955 {
956 /*
957 * FIXME:
958 * Try to page out this page and set the swap entry
959 * within the section segment. There exist no rmap entry
960 * for this page. The pager thread can't page out a
961 * page without a rmap entry.
962 */
963 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
964 if (InEntry) *InEntry = Entry;
965 MiSetPageEvent(NULL, NULL);
966 }
967 else
968 {
969 MmSetPageEntrySectionSegment(Segment, Offset, 0);
970 if (InEntry) *InEntry = 0;
971 MiSetPageEvent(NULL, NULL);
972 if (!IsDirectMapped)
973 {
974 MmReleasePageMemoryConsumer(MC_USER, Page);
975 }
976 }
977 }
978 else
979 {
980 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
981 (Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
982 {
983 if (!PageOut)
984 {
985 if (Dirty)
986 {
987 /*
988 * FIXME:
989 * We hold all locks. Nobody can do something with the current
990 * process and the current segment (also not within an other process).
991 */
992 NTSTATUS Status;
993 Status = MmWriteToSwapPage(SavedSwapEntry, Page);
994 if (!NT_SUCCESS(Status))
995 {
996 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n", Status);
997 KeBugCheck(MEMORY_MANAGEMENT);
998 }
999 }
1000 MmSetPageEntrySectionSegment(Segment, Offset, MAKE_SWAP_SSE(SavedSwapEntry));
1001 if (InEntry) *InEntry = MAKE_SWAP_SSE(SavedSwapEntry);
1002 MmSetSavedSwapEntryPage(Page, 0);
1003 MiSetPageEvent(NULL, NULL);
1004 }
1005 MmReleasePageMemoryConsumer(MC_USER, Page);
1006 }
1007 else
1008 {
1009 DPRINT1("Found a swapentry for a non private page in an image or data file sgment\n");
1010 KeBugCheck(MEMORY_MANAGEMENT);
1011 }
1012 }
1013 }
1014 else
1015 {
1016 if (InEntry)
1017 *InEntry = Entry;
1018 else
1019 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
1020 }
1021 return(SHARE_COUNT_FROM_SSE(Entry) > 0);
1022 }
1023
1024 BOOLEAN MiIsPageFromCache(PMEMORY_AREA MemoryArea,
1025 LONGLONG SegOffset)
1026 {
1027 #ifndef NEWCC
1028 if (!(MemoryArea->Data.SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1029 {
1030 PROS_SHARED_CACHE_MAP SharedCacheMap;
1031 PROS_VACB Vacb;
1032 SharedCacheMap = MemoryArea->Data.SectionData.Section->FileObject->SectionObjectPointer->SharedCacheMap;
1033 Vacb = CcRosLookupVacb(SharedCacheMap, SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset);
1034 if (Vacb)
1035 {
1036 CcRosReleaseVacb(SharedCacheMap, Vacb, Vacb->Valid, FALSE, TRUE);
1037 return TRUE;
1038 }
1039 }
1040 #endif
1041 return FALSE;
1042 }
1043
1044 NTSTATUS
1045 NTAPI
1046 MiCopyFromUserPage(PFN_NUMBER DestPage, PFN_NUMBER SrcPage)
1047 {
1048 PEPROCESS Process;
1049 KIRQL Irql, Irql2;
1050 PVOID DestAddress, SrcAddress;
1051
1052 Process = PsGetCurrentProcess();
1053 DestAddress = MiMapPageInHyperSpace(Process, DestPage, &Irql);
1054 SrcAddress = MiMapPageInHyperSpace(Process, SrcPage, &Irql2);
1055 if (DestAddress == NULL || SrcAddress == 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, SrcAddress, Irql2);
1063 MiUnmapPageInHyperSpace(Process, DestAddress, Irql);
1064 return(STATUS_SUCCESS);
1065 }
1066
1067 #ifndef NEWCC
1068 NTSTATUS
1069 NTAPI
1070 MiReadPage(PMEMORY_AREA MemoryArea,
1071 LONGLONG SegOffset,
1072 PPFN_NUMBER Page)
1073 /*
1074 * FUNCTION: Read a page for a section backed memory area.
1075 * PARAMETERS:
1076 * MemoryArea - Memory area to read the page for.
1077 * Offset - Offset of the page to read.
1078 * Page - Variable that receives a page contains the read data.
1079 */
1080 {
1081 LONGLONG BaseOffset;
1082 LONGLONG FileOffset;
1083 PVOID BaseAddress;
1084 BOOLEAN UptoDate;
1085 PROS_VACB Vacb;
1086 PFILE_OBJECT FileObject;
1087 NTSTATUS Status;
1088 LONGLONG RawLength;
1089 PROS_SHARED_CACHE_MAP SharedCacheMap;
1090 BOOLEAN IsImageSection;
1091 LONGLONG Length;
1092
1093 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
1094 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1095 RawLength = MemoryArea->Data.SectionData.Segment->RawLength.QuadPart;
1096 FileOffset = SegOffset + MemoryArea->Data.SectionData.Segment->Image.FileOffset;
1097 IsImageSection = MemoryArea->Data.SectionData.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1098
1099 ASSERT(SharedCacheMap);
1100
1101 DPRINT("%S %I64x\n", FileObject->FileName.Buffer, FileOffset);
1102
1103 /*
1104 * If the file system is letting us go directly to the cache and the
1105 * memory area was mapped at an offset in the file which is page aligned
1106 * then get the related VACB.
1107 */
1108 if (((FileOffset % PAGE_SIZE) == 0) &&
1109 ((SegOffset + PAGE_SIZE <= RawLength) || !IsImageSection) &&
1110 !(MemoryArea->Data.SectionData.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1111 {
1112
1113 /*
1114 * Get the related VACB; we use a lower level interface than
1115 * filesystems do because it is safe for us to use an offset with an
1116 * alignment less than the file system block size.
1117 */
1118 Status = CcRosGetVacb(SharedCacheMap,
1119 FileOffset,
1120 &BaseOffset,
1121 &BaseAddress,
1122 &UptoDate,
1123 &Vacb);
1124 if (!NT_SUCCESS(Status))
1125 {
1126 return(Status);
1127 }
1128 if (!UptoDate)
1129 {
1130 /*
1131 * If the VACB isn't up to date then call the file
1132 * system to read in the data.
1133 */
1134 Status = CcReadVirtualAddress(Vacb);
1135 if (!NT_SUCCESS(Status))
1136 {
1137 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1138 return Status;
1139 }
1140 }
1141
1142 /* Probe the page, since it's PDE might not be synced */
1143 (void)*((volatile char*)BaseAddress + FileOffset - BaseOffset);
1144
1145 /*
1146 * Retrieve the page from the view that we actually want.
1147 */
1148 (*Page) = MmGetPhysicalAddress((char*)BaseAddress +
1149 FileOffset - BaseOffset).LowPart >> PAGE_SHIFT;
1150
1151 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, TRUE);
1152 }
1153 else
1154 {
1155 PEPROCESS Process;
1156 KIRQL Irql;
1157 PVOID PageAddr;
1158 LONGLONG VacbOffset;
1159
1160 /*
1161 * Allocate a page, this is rather complicated by the possibility
1162 * we might have to move other things out of memory
1163 */
1164 MI_SET_USAGE(MI_USAGE_SECTION);
1165 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
1166 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
1167 if (!NT_SUCCESS(Status))
1168 {
1169 return(Status);
1170 }
1171 Status = CcRosGetVacb(SharedCacheMap,
1172 FileOffset,
1173 &BaseOffset,
1174 &BaseAddress,
1175 &UptoDate,
1176 &Vacb);
1177 if (!NT_SUCCESS(Status))
1178 {
1179 return(Status);
1180 }
1181 if (!UptoDate)
1182 {
1183 /*
1184 * If the VACB isn't up to date then call the file
1185 * system to read in the data.
1186 */
1187 Status = CcReadVirtualAddress(Vacb);
1188 if (!NT_SUCCESS(Status))
1189 {
1190 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1191 return Status;
1192 }
1193 }
1194
1195 Process = PsGetCurrentProcess();
1196 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1197 VacbOffset = BaseOffset + VACB_MAPPING_GRANULARITY - FileOffset;
1198 Length = RawLength - SegOffset;
1199 if (Length <= VacbOffset && Length <= PAGE_SIZE)
1200 {
1201 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
1202 }
1203 else if (VacbOffset >= PAGE_SIZE)
1204 {
1205 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
1206 }
1207 else
1208 {
1209 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, VacbOffset);
1210 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1211 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
1212 Status = CcRosGetVacb(SharedCacheMap,
1213 FileOffset + VacbOffset,
1214 &BaseOffset,
1215 &BaseAddress,
1216 &UptoDate,
1217 &Vacb);
1218 if (!NT_SUCCESS(Status))
1219 {
1220 return(Status);
1221 }
1222 if (!UptoDate)
1223 {
1224 /*
1225 * If the VACB isn't up to date then call the file
1226 * system to read in the data.
1227 */
1228 Status = CcReadVirtualAddress(Vacb);
1229 if (!NT_SUCCESS(Status))
1230 {
1231 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1232 return Status;
1233 }
1234 }
1235 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1236 if (Length < PAGE_SIZE)
1237 {
1238 memcpy((char*)PageAddr + VacbOffset, BaseAddress, Length - VacbOffset);
1239 }
1240 else
1241 {
1242 memcpy((char*)PageAddr + VacbOffset, BaseAddress, PAGE_SIZE - VacbOffset);
1243 }
1244 }
1245 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1246 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
1247 }
1248 return(STATUS_SUCCESS);
1249 }
1250 #else
1251 NTSTATUS
1252 NTAPI
1253 MiReadPage(PMEMORY_AREA MemoryArea,
1254 LONGLONG SegOffset,
1255 PPFN_NUMBER Page)
1256 /*
1257 * FUNCTION: Read a page for a section backed memory area.
1258 * PARAMETERS:
1259 * MemoryArea - Memory area to read the page for.
1260 * Offset - Offset of the page to read.
1261 * Page - Variable that receives a page contains the read data.
1262 */
1263 {
1264 MM_REQUIRED_RESOURCES Resources;
1265 NTSTATUS Status;
1266
1267 RtlZeroMemory(&Resources, sizeof(MM_REQUIRED_RESOURCES));
1268
1269 Resources.Context = MemoryArea->Data.SectionData.Section->FileObject;
1270 Resources.FileOffset.QuadPart = SegOffset +
1271 MemoryArea->Data.SectionData.Segment->Image.FileOffset;
1272 Resources.Consumer = MC_USER;
1273 Resources.Amount = PAGE_SIZE;
1274
1275 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]);
1276
1277 Status = MiReadFilePage(MmGetKernelAddressSpace(), MemoryArea, &Resources);
1278 *Page = Resources.Page[0];
1279 return Status;
1280 }
1281 #endif
1282
1283 NTSTATUS
1284 NTAPI
1285 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1286 MEMORY_AREA* MemoryArea,
1287 PVOID Address,
1288 BOOLEAN Locked)
1289 {
1290 LARGE_INTEGER Offset;
1291 PFN_NUMBER Page;
1292 NTSTATUS Status;
1293 PROS_SECTION_OBJECT Section;
1294 PMM_SECTION_SEGMENT Segment;
1295 ULONG_PTR Entry;
1296 ULONG_PTR Entry1;
1297 ULONG Attributes;
1298 PMM_REGION Region;
1299 BOOLEAN HasSwapEntry;
1300 PVOID PAddress;
1301 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1302 SWAPENTRY SwapEntry;
1303
1304 /*
1305 * There is a window between taking the page fault and locking the
1306 * address space when another thread could load the page so we check
1307 * that.
1308 */
1309 if (MmIsPagePresent(Process, Address))
1310 {
1311 return(STATUS_SUCCESS);
1312 }
1313
1314 if (MmIsDisabledPage(Process, Address))
1315 {
1316 return(STATUS_ACCESS_VIOLATION);
1317 }
1318
1319 /*
1320 * Check for the virtual memory area being deleted.
1321 */
1322 if (MemoryArea->DeleteInProgress)
1323 {
1324 return(STATUS_UNSUCCESSFUL);
1325 }
1326
1327 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1328 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1329 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1330
1331 Segment = MemoryArea->Data.SectionData.Segment;
1332 Section = MemoryArea->Data.SectionData.Section;
1333 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1334 &MemoryArea->Data.SectionData.RegionListHead,
1335 Address, NULL);
1336 ASSERT(Region != NULL);
1337 /*
1338 * Lock the segment
1339 */
1340 MmLockSectionSegment(Segment);
1341 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1342 /*
1343 * Check if this page needs to be mapped COW
1344 */
1345 if ((Segment->WriteCopy) &&
1346 (Region->Protect == PAGE_READWRITE ||
1347 Region->Protect == PAGE_EXECUTE_READWRITE))
1348 {
1349 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1350 }
1351 else
1352 {
1353 Attributes = Region->Protect;
1354 }
1355
1356 /*
1357 * Check if someone else is already handling this fault, if so wait
1358 * for them
1359 */
1360 if (Entry && IS_SWAP_FROM_SSE(Entry) && SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY)
1361 {
1362 MmUnlockSectionSegment(Segment);
1363 MmUnlockAddressSpace(AddressSpace);
1364 MiWaitForPageEvent(NULL, NULL);
1365 MmLockAddressSpace(AddressSpace);
1366 DPRINT("Address 0x%p\n", Address);
1367 return(STATUS_MM_RESTART_OPERATION);
1368 }
1369
1370 HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1371
1372 if (HasSwapEntry)
1373 {
1374 SWAPENTRY DummyEntry;
1375
1376 /*
1377 * Is it a wait entry?
1378 */
1379 MmGetPageFileMapping(Process, Address, &SwapEntry);
1380
1381 if (SwapEntry == MM_WAIT_ENTRY)
1382 {
1383 MmUnlockSectionSegment(Segment);
1384 MmUnlockAddressSpace(AddressSpace);
1385 MiWaitForPageEvent(NULL, NULL);
1386 MmLockAddressSpace(AddressSpace);
1387 return STATUS_MM_RESTART_OPERATION;
1388 }
1389
1390 /*
1391 * Must be private page we have swapped out.
1392 */
1393
1394 /*
1395 * Sanity check
1396 */
1397 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
1398 {
1399 DPRINT1("Found a swaped out private page in a pagefile section.\n");
1400 KeBugCheck(MEMORY_MANAGEMENT);
1401 }
1402
1403 MmUnlockSectionSegment(Segment);
1404 MmDeletePageFileMapping(Process, Address, &SwapEntry);
1405 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1406
1407 MmUnlockAddressSpace(AddressSpace);
1408 MI_SET_USAGE(MI_USAGE_SECTION);
1409 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1410 if (!Process) MI_SET_PROCESS2("Kernel Section");
1411 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1412 if (!NT_SUCCESS(Status))
1413 {
1414 KeBugCheck(MEMORY_MANAGEMENT);
1415 }
1416
1417 Status = MmReadFromSwapPage(SwapEntry, Page);
1418 if (!NT_SUCCESS(Status))
1419 {
1420 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1421 KeBugCheck(MEMORY_MANAGEMENT);
1422 }
1423 MmLockAddressSpace(AddressSpace);
1424 MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1425 Status = MmCreateVirtualMapping(Process,
1426 PAddress,
1427 Region->Protect,
1428 &Page,
1429 1);
1430 if (!NT_SUCCESS(Status))
1431 {
1432 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1433 KeBugCheck(MEMORY_MANAGEMENT);
1434 return(Status);
1435 }
1436
1437 /*
1438 * Store the swap entry for later use.
1439 */
1440 MmSetSavedSwapEntryPage(Page, SwapEntry);
1441
1442 /*
1443 * Add the page to the process's working set
1444 */
1445 MmInsertRmap(Page, Process, Address);
1446 /*
1447 * Finish the operation
1448 */
1449 MiSetPageEvent(Process, Address);
1450 DPRINT("Address 0x%p\n", Address);
1451 return(STATUS_SUCCESS);
1452 }
1453
1454 /*
1455 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1456 */
1457 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1458 {
1459 MmUnlockSectionSegment(Segment);
1460 /*
1461 * Just map the desired physical page
1462 */
1463 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1464 Status = MmCreateVirtualMappingUnsafe(Process,
1465 PAddress,
1466 Region->Protect,
1467 &Page,
1468 1);
1469 if (!NT_SUCCESS(Status))
1470 {
1471 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1472 KeBugCheck(MEMORY_MANAGEMENT);
1473 return(Status);
1474 }
1475
1476 /*
1477 * Cleanup and release locks
1478 */
1479 MiSetPageEvent(Process, Address);
1480 DPRINT("Address 0x%p\n", Address);
1481 return(STATUS_SUCCESS);
1482 }
1483
1484 /*
1485 * Get the entry corresponding to the offset within the section
1486 */
1487 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1488
1489 if (Entry == 0)
1490 {
1491 SWAPENTRY FakeSwapEntry;
1492
1493 /*
1494 * If the entry is zero (and it can't change because we have
1495 * locked the segment) then we need to load the page.
1496 */
1497
1498 /*
1499 * Release all our locks and read in the page from disk
1500 */
1501 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1502 MmUnlockSectionSegment(Segment);
1503 MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY);
1504 MmUnlockAddressSpace(AddressSpace);
1505
1506 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1507 ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart) &&
1508 (Section->AllocationAttributes & SEC_IMAGE))))
1509 {
1510 MI_SET_USAGE(MI_USAGE_SECTION);
1511 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1512 if (!Process) MI_SET_PROCESS2("Kernel Section");
1513 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1514 if (!NT_SUCCESS(Status))
1515 {
1516 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
1517 }
1518
1519 }
1520 else
1521 {
1522 Status = MiReadPage(MemoryArea, Offset.QuadPart, &Page);
1523 if (!NT_SUCCESS(Status))
1524 {
1525 DPRINT1("MiReadPage failed (Status %x)\n", Status);
1526 }
1527 }
1528 if (!NT_SUCCESS(Status))
1529 {
1530 /*
1531 * FIXME: What do we know in this case?
1532 */
1533 /*
1534 * Cleanup and release locks
1535 */
1536 MmLockAddressSpace(AddressSpace);
1537 MiSetPageEvent(Process, Address);
1538 DPRINT("Address 0x%p\n", Address);
1539 return(Status);
1540 }
1541
1542 /*
1543 * Mark the offset within the section as having valid, in-memory
1544 * data
1545 */
1546 MmLockAddressSpace(AddressSpace);
1547 MmLockSectionSegment(Segment);
1548 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1549 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1550 MmUnlockSectionSegment(Segment);
1551
1552 MmDeletePageFileMapping(Process, PAddress, &FakeSwapEntry);
1553 DPRINT("CreateVirtualMapping Page %x Process %p PAddress %p Attributes %x\n",
1554 Page, Process, PAddress, Attributes);
1555 Status = MmCreateVirtualMapping(Process,
1556 PAddress,
1557 Attributes,
1558 &Page,
1559 1);
1560 if (!NT_SUCCESS(Status))
1561 {
1562 DPRINT1("Unable to create virtual mapping\n");
1563 KeBugCheck(MEMORY_MANAGEMENT);
1564 }
1565 ASSERT(MmIsPagePresent(Process, PAddress));
1566 MmInsertRmap(Page, Process, Address);
1567
1568 MiSetPageEvent(Process, Address);
1569 DPRINT("Address 0x%p\n", Address);
1570 return(STATUS_SUCCESS);
1571 }
1572 else if (IS_SWAP_FROM_SSE(Entry))
1573 {
1574 SWAPENTRY SwapEntry;
1575
1576 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1577
1578 /*
1579 * Release all our locks and read in the page from disk
1580 */
1581 MmUnlockSectionSegment(Segment);
1582
1583 MmUnlockAddressSpace(AddressSpace);
1584 MI_SET_USAGE(MI_USAGE_SECTION);
1585 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1586 if (!Process) MI_SET_PROCESS2("Kernel Section");
1587 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1588 if (!NT_SUCCESS(Status))
1589 {
1590 KeBugCheck(MEMORY_MANAGEMENT);
1591 }
1592
1593 Status = MmReadFromSwapPage(SwapEntry, Page);
1594 if (!NT_SUCCESS(Status))
1595 {
1596 KeBugCheck(MEMORY_MANAGEMENT);
1597 }
1598
1599 /*
1600 * Relock the address space and segment
1601 */
1602 MmLockAddressSpace(AddressSpace);
1603 MmLockSectionSegment(Segment);
1604
1605 /*
1606 * Check the entry. No one should change the status of a page
1607 * that has a pending page-in.
1608 */
1609 Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
1610 if (Entry != Entry1)
1611 {
1612 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1613 KeBugCheck(MEMORY_MANAGEMENT);
1614 }
1615
1616 /*
1617 * Mark the offset within the section as having valid, in-memory
1618 * data
1619 */
1620 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1621 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1622 MmUnlockSectionSegment(Segment);
1623
1624 /*
1625 * Save the swap entry.
1626 */
1627 MmSetSavedSwapEntryPage(Page, SwapEntry);
1628 Status = MmCreateVirtualMapping(Process,
1629 PAddress,
1630 Region->Protect,
1631 &Page,
1632 1);
1633 if (!NT_SUCCESS(Status))
1634 {
1635 DPRINT1("Unable to create virtual mapping\n");
1636 KeBugCheck(MEMORY_MANAGEMENT);
1637 }
1638 MmInsertRmap(Page, Process, Address);
1639 MiSetPageEvent(Process, Address);
1640 DPRINT("Address 0x%p\n", Address);
1641 return(STATUS_SUCCESS);
1642 }
1643 else
1644 {
1645 /*
1646 * If the section offset is already in-memory and valid then just
1647 * take another reference to the page
1648 */
1649
1650 Page = PFN_FROM_SSE(Entry);
1651
1652 MmSharePageEntrySectionSegment(Segment, &Offset);
1653 MmUnlockSectionSegment(Segment);
1654
1655 Status = MmCreateVirtualMapping(Process,
1656 PAddress,
1657 Attributes,
1658 &Page,
1659 1);
1660 if (!NT_SUCCESS(Status))
1661 {
1662 DPRINT1("Unable to create virtual mapping\n");
1663 KeBugCheck(MEMORY_MANAGEMENT);
1664 }
1665 MmInsertRmap(Page, Process, Address);
1666 MiSetPageEvent(Process, Address);
1667 DPRINT("Address 0x%p\n", Address);
1668 return(STATUS_SUCCESS);
1669 }
1670 }
1671
1672 NTSTATUS
1673 NTAPI
1674 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1675 MEMORY_AREA* MemoryArea,
1676 PVOID Address)
1677 {
1678 PMM_SECTION_SEGMENT Segment;
1679 PROS_SECTION_OBJECT Section;
1680 PFN_NUMBER OldPage;
1681 PFN_NUMBER NewPage;
1682 NTSTATUS Status;
1683 PVOID PAddress;
1684 LARGE_INTEGER Offset;
1685 PMM_REGION Region;
1686 ULONG_PTR Entry;
1687 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1688 SWAPENTRY SwapEntry;
1689
1690 DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1691
1692 /*
1693 * Check if the page has already been set readwrite
1694 */
1695 if (MmGetPageProtect(Process, Address) & PAGE_READWRITE)
1696 {
1697 DPRINT("Address 0x%p\n", Address);
1698 return(STATUS_SUCCESS);
1699 }
1700
1701 /*
1702 * Find the offset of the page
1703 */
1704 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1705 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1706 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1707
1708 Segment = MemoryArea->Data.SectionData.Segment;
1709 Section = MemoryArea->Data.SectionData.Section;
1710 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1711 &MemoryArea->Data.SectionData.RegionListHead,
1712 Address, NULL);
1713 ASSERT(Region != NULL);
1714 /*
1715 * Lock the segment
1716 */
1717 MmLockSectionSegment(Segment);
1718
1719 OldPage = MmGetPfnForProcess(Process, Address);
1720 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1721
1722 MmUnlockSectionSegment(Segment);
1723
1724 /*
1725 * Check if we are doing COW
1726 */
1727 if (!((Segment->WriteCopy) &&
1728 (Region->Protect == PAGE_READWRITE ||
1729 Region->Protect == PAGE_EXECUTE_READWRITE)))
1730 {
1731 DPRINT("Address 0x%p\n", Address);
1732 return(STATUS_ACCESS_VIOLATION);
1733 }
1734
1735 if (IS_SWAP_FROM_SSE(Entry) ||
1736 PFN_FROM_SSE(Entry) != OldPage)
1737 {
1738 /* This is a private page. We must only change the page protection. */
1739 MmSetPageProtect(Process, Address, Region->Protect);
1740 return(STATUS_SUCCESS);
1741 }
1742
1743 if(OldPage == 0)
1744 DPRINT("OldPage == 0!\n");
1745
1746 /*
1747 * Get or create a pageop
1748 */
1749 MmLockSectionSegment(Segment);
1750 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1751
1752 /*
1753 * Wait for any other operations to complete
1754 */
1755 if (Entry == SWAPENTRY_FROM_SSE(MM_WAIT_ENTRY))
1756 {
1757 MmUnlockSectionSegment(Segment);
1758 MmUnlockAddressSpace(AddressSpace);
1759 MiWaitForPageEvent(NULL, NULL);
1760 /*
1761 * Restart the operation
1762 */
1763 MmLockAddressSpace(AddressSpace);
1764 DPRINT("Address 0x%p\n", Address);
1765 return(STATUS_MM_RESTART_OPERATION);
1766 }
1767
1768 MmDeleteRmap(OldPage, Process, PAddress);
1769 MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
1770 MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY);
1771
1772 /*
1773 * Release locks now we have the pageop
1774 */
1775 MmUnlockSectionSegment(Segment);
1776 MmUnlockAddressSpace(AddressSpace);
1777
1778 /*
1779 * Allocate a page
1780 */
1781 MI_SET_USAGE(MI_USAGE_SECTION);
1782 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1783 if (!Process) MI_SET_PROCESS2("Kernel Section");
1784 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1785 if (!NT_SUCCESS(Status))
1786 {
1787 KeBugCheck(MEMORY_MANAGEMENT);
1788 }
1789
1790 /*
1791 * Copy the old page
1792 */
1793 MiCopyFromUserPage(NewPage, OldPage);
1794
1795 MmLockAddressSpace(AddressSpace);
1796
1797 /*
1798 * Set the PTE to point to the new page
1799 */
1800 MmDeletePageFileMapping(Process, PAddress, &SwapEntry);
1801 Status = MmCreateVirtualMapping(Process,
1802 PAddress,
1803 Region->Protect,
1804 &NewPage,
1805 1);
1806 if (!NT_SUCCESS(Status))
1807 {
1808 DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
1809 KeBugCheck(MEMORY_MANAGEMENT);
1810 return(Status);
1811 }
1812
1813 /*
1814 * Unshare the old page.
1815 */
1816 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
1817 MmInsertRmap(NewPage, Process, PAddress);
1818 MmLockSectionSegment(Segment);
1819 MmUnsharePageEntrySectionSegment(Section, Segment, &Offset, FALSE, FALSE, NULL);
1820 MmUnlockSectionSegment(Segment);
1821
1822 MiSetPageEvent(Process, Address);
1823 DPRINT("Address 0x%p\n", Address);
1824 return(STATUS_SUCCESS);
1825 }
1826
1827 VOID
1828 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1829 {
1830 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1831 BOOLEAN WasDirty;
1832 PFN_NUMBER Page = 0;
1833
1834 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1835 if (Process)
1836 {
1837 MmLockAddressSpace(&Process->Vm);
1838 }
1839
1840 MmDeleteVirtualMapping(Process,
1841 Address,
1842 &WasDirty,
1843 &Page);
1844 if (WasDirty)
1845 {
1846 PageOutContext->WasDirty = TRUE;
1847 }
1848 if (!PageOutContext->Private)
1849 {
1850 MmLockSectionSegment(PageOutContext->Segment);
1851 MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section,
1852 PageOutContext->Segment,
1853 &PageOutContext->Offset,
1854 PageOutContext->WasDirty,
1855 TRUE,
1856 &PageOutContext->SectionEntry);
1857 MmUnlockSectionSegment(PageOutContext->Segment);
1858 }
1859 if (Process)
1860 {
1861 MmUnlockAddressSpace(&Process->Vm);
1862 }
1863
1864 if (PageOutContext->Private)
1865 {
1866 MmReleasePageMemoryConsumer(MC_USER, Page);
1867 }
1868 }
1869
1870 NTSTATUS
1871 NTAPI
1872 MmPageOutSectionView(PMMSUPPORT AddressSpace,
1873 MEMORY_AREA* MemoryArea,
1874 PVOID Address, ULONG_PTR Entry)
1875 {
1876 PFN_NUMBER Page;
1877 MM_SECTION_PAGEOUT_CONTEXT Context;
1878 SWAPENTRY SwapEntry;
1879 NTSTATUS Status;
1880 #ifndef NEWCC
1881 ULONGLONG FileOffset;
1882 PFILE_OBJECT FileObject;
1883 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
1884 BOOLEAN IsImageSection;
1885 #endif
1886 BOOLEAN DirectMapped;
1887 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1888 KIRQL OldIrql;
1889
1890 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1891
1892 /*
1893 * Get the segment and section.
1894 */
1895 Context.Segment = MemoryArea->Data.SectionData.Segment;
1896 Context.Section = MemoryArea->Data.SectionData.Section;
1897 Context.SectionEntry = Entry;
1898 Context.CallingProcess = Process;
1899
1900 Context.Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1901 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1902
1903 DirectMapped = FALSE;
1904
1905 MmLockSectionSegment(Context.Segment);
1906
1907 #ifndef NEWCC
1908 FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset;
1909 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1910 FileObject = Context.Section->FileObject;
1911
1912 if (FileObject != NULL &&
1913 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1914 {
1915 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1916
1917 /*
1918 * If the file system is letting us go directly to the cache and the
1919 * memory area was mapped at an offset in the file which is page aligned
1920 * then note this is a direct mapped page.
1921 */
1922 if ((FileOffset % PAGE_SIZE) == 0 &&
1923 (Context.Offset.QuadPart + PAGE_SIZE <= Context.Segment->RawLength.QuadPart || !IsImageSection))
1924 {
1925 DirectMapped = TRUE;
1926 }
1927 }
1928 #endif
1929
1930
1931 /*
1932 * This should never happen since mappings of physical memory are never
1933 * placed in the rmap lists.
1934 */
1935 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1936 {
1937 DPRINT1("Trying to page out from physical memory section address 0x%p "
1938 "process %p\n", Address,
1939 Process ? Process->UniqueProcessId : 0);
1940 KeBugCheck(MEMORY_MANAGEMENT);
1941 }
1942
1943 /*
1944 * Get the section segment entry and the physical address.
1945 */
1946 if (!MmIsPagePresent(Process, Address))
1947 {
1948 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
1949 Process ? Process->UniqueProcessId : 0, Address);
1950 KeBugCheck(MEMORY_MANAGEMENT);
1951 }
1952 Page = MmGetPfnForProcess(Process, Address);
1953 SwapEntry = MmGetSavedSwapEntryPage(Page);
1954
1955 /*
1956 * Check the reference count to ensure this page can be paged out
1957 */
1958 if (MmGetReferenceCountPage(Page) != 1)
1959 {
1960 DPRINT("Cannot page out locked section page: 0x%lu (RefCount: %lu)\n",
1961 Page, MmGetReferenceCountPage(Page));
1962 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
1963 MmUnlockSectionSegment(Context.Segment);
1964 return STATUS_UNSUCCESSFUL;
1965 }
1966
1967 /*
1968 * Prepare the context structure for the rmap delete call.
1969 */
1970 MmUnlockSectionSegment(Context.Segment);
1971 Context.WasDirty = FALSE;
1972 if (Context.Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1973 IS_SWAP_FROM_SSE(Entry) ||
1974 PFN_FROM_SSE(Entry) != Page)
1975 {
1976 Context.Private = TRUE;
1977 }
1978 else
1979 {
1980 Context.Private = FALSE;
1981 }
1982
1983 /*
1984 * Take an additional reference to the page or the VACB.
1985 */
1986 if (DirectMapped && !Context.Private)
1987 {
1988 if(!MiIsPageFromCache(MemoryArea, Context.Offset.QuadPart))
1989 {
1990 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
1991 KeBugCheck(MEMORY_MANAGEMENT);
1992 }
1993 }
1994 else
1995 {
1996 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1997 MmReferencePage(Page);
1998 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1999 }
2000
2001 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
2002
2003 /* Since we passed in a surrogate, we'll get back the page entry
2004 * state in our context. This is intended to make intermediate
2005 * decrements of share count not release the wait entry.
2006 */
2007 Entry = Context.SectionEntry;
2008
2009 /*
2010 * If this wasn't a private page then we should have reduced the entry to
2011 * zero by deleting all the rmaps.
2012 */
2013 if (!Context.Private && Entry != 0)
2014 {
2015 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
2016 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2017 {
2018 KeBugCheckEx(MEMORY_MANAGEMENT, Entry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2019 }
2020 }
2021
2022 /*
2023 * If the page wasn't dirty then we can just free it as for a readonly page.
2024 * Since we unmapped all the mappings above we know it will not suddenly
2025 * become dirty.
2026 * If the page is from a pagefile section and has no swap entry,
2027 * we can't free the page at this point.
2028 */
2029 SwapEntry = MmGetSavedSwapEntryPage(Page);
2030 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
2031 {
2032 if (Context.Private)
2033 {
2034 DPRINT1("Found a %s private page (address %p) in a pagefile segment.\n",
2035 Context.WasDirty ? "dirty" : "clean", Address);
2036 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2037 }
2038 if (!Context.WasDirty && SwapEntry != 0)
2039 {
2040 MmSetSavedSwapEntryPage(Page, 0);
2041 MmLockSectionSegment(Context.Segment);
2042 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2043 MmUnlockSectionSegment(Context.Segment);
2044 MmReleasePageMemoryConsumer(MC_USER, Page);
2045 MiSetPageEvent(NULL, NULL);
2046 return(STATUS_SUCCESS);
2047 }
2048 }
2049 else if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2050 {
2051 if (Context.Private)
2052 {
2053 DPRINT1("Found a %s private page (address %p) in a shared section segment.\n",
2054 Context.WasDirty ? "dirty" : "clean", Address);
2055 KeBugCheckEx(MEMORY_MANAGEMENT, Page, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2056 }
2057 if (!Context.WasDirty || SwapEntry != 0)
2058 {
2059 MmSetSavedSwapEntryPage(Page, 0);
2060 if (SwapEntry != 0)
2061 {
2062 MmLockSectionSegment(Context.Segment);
2063 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2064 MmUnlockSectionSegment(Context.Segment);
2065 }
2066 MmReleasePageMemoryConsumer(MC_USER, Page);
2067 MiSetPageEvent(NULL, NULL);
2068 return(STATUS_SUCCESS);
2069 }
2070 }
2071 else if (!Context.Private && DirectMapped)
2072 {
2073 if (SwapEntry != 0)
2074 {
2075 DPRINT1("Found a swapentry for a non private and direct mapped page (address %p)\n",
2076 Address);
2077 KeBugCheckEx(MEMORY_MANAGEMENT, STATUS_UNSUCCESSFUL, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address);
2078 }
2079 #ifndef NEWCC
2080 Status = CcRosUnmapVacb(SharedCacheMap, FileOffset, FALSE);
2081 #else
2082 Status = STATUS_SUCCESS;
2083 #endif
2084 #ifndef NEWCC
2085 if (!NT_SUCCESS(Status))
2086 {
2087 DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
2088 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)SharedCacheMap, (ULONG_PTR)FileOffset, (ULONG_PTR)Address);
2089 }
2090 #endif
2091 MiSetPageEvent(NULL, NULL);
2092 return(STATUS_SUCCESS);
2093 }
2094 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
2095 {
2096 if (SwapEntry != 0)
2097 {
2098 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %p)\n",
2099 Address);
2100 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, Page, (ULONG_PTR)Process, (ULONG_PTR)Address);
2101 }
2102 MmReleasePageMemoryConsumer(MC_USER, Page);
2103 MiSetPageEvent(NULL, NULL);
2104 return(STATUS_SUCCESS);
2105 }
2106 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
2107 {
2108 DPRINT("Not dirty and private and not swapped (%p:%p)\n", Process, Address);
2109 MmSetSavedSwapEntryPage(Page, 0);
2110 MmLockAddressSpace(AddressSpace);
2111 Status = MmCreatePageFileMapping(Process,
2112 Address,
2113 SwapEntry);
2114 MmUnlockAddressSpace(AddressSpace);
2115 if (!NT_SUCCESS(Status))
2116 {
2117 DPRINT1("Status %x Swapping out %p:%p\n", Status, Process, Address);
2118 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2119 }
2120 MmReleasePageMemoryConsumer(MC_USER, Page);
2121 MiSetPageEvent(NULL, NULL);
2122 return(STATUS_SUCCESS);
2123 }
2124
2125 /*
2126 * If necessary, allocate an entry in the paging file for this page
2127 */
2128 if (SwapEntry == 0)
2129 {
2130 SwapEntry = MmAllocSwapPage();
2131 if (SwapEntry == 0)
2132 {
2133 MmShowOutOfSpaceMessagePagingFile();
2134 MmLockAddressSpace(AddressSpace);
2135 /*
2136 * For private pages restore the old mappings.
2137 */
2138 if (Context.Private)
2139 {
2140 Status = MmCreateVirtualMapping(Process,
2141 Address,
2142 MemoryArea->Protect,
2143 &Page,
2144 1);
2145 MmSetDirtyPage(Process, Address);
2146 MmInsertRmap(Page,
2147 Process,
2148 Address);
2149 }
2150 else
2151 {
2152 ULONG_PTR OldEntry;
2153 /*
2154 * For non-private pages if the page wasn't direct mapped then
2155 * set it back into the section segment entry so we don't loose
2156 * our copy. Otherwise it will be handled by the cache manager.
2157 */
2158 Status = MmCreateVirtualMapping(Process,
2159 Address,
2160 MemoryArea->Protect,
2161 &Page,
2162 1);
2163 MmSetDirtyPage(Process, Address);
2164 MmInsertRmap(Page,
2165 Process,
2166 Address);
2167 // If we got here, the previous entry should have been a wait
2168 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2169 MmLockSectionSegment(Context.Segment);
2170 OldEntry = MmGetPageEntrySectionSegment(Context.Segment, &Context.Offset);
2171 ASSERT(OldEntry == 0 || OldEntry == MAKE_SWAP_SSE(MM_WAIT_ENTRY));
2172 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2173 MmUnlockSectionSegment(Context.Segment);
2174 }
2175 MmUnlockAddressSpace(AddressSpace);
2176 MiSetPageEvent(NULL, NULL);
2177 return(STATUS_PAGEFILE_QUOTA);
2178 }
2179 }
2180
2181 /*
2182 * Write the page to the pagefile
2183 */
2184 Status = MmWriteToSwapPage(SwapEntry, Page);
2185 if (!NT_SUCCESS(Status))
2186 {
2187 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2188 Status);
2189 /*
2190 * As above: undo our actions.
2191 * FIXME: Also free the swap page.
2192 */
2193 MmLockAddressSpace(AddressSpace);
2194 if (Context.Private)
2195 {
2196 Status = MmCreateVirtualMapping(Process,
2197 Address,
2198 MemoryArea->Protect,
2199 &Page,
2200 1);
2201 MmSetDirtyPage(Process, Address);
2202 MmInsertRmap(Page,
2203 Process,
2204 Address);
2205 }
2206 else
2207 {
2208 Status = MmCreateVirtualMapping(Process,
2209 Address,
2210 MemoryArea->Protect,
2211 &Page,
2212 1);
2213 MmSetDirtyPage(Process, Address);
2214 MmInsertRmap(Page,
2215 Process,
2216 Address);
2217 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2218 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2219 }
2220 MmUnlockAddressSpace(AddressSpace);
2221 MiSetPageEvent(NULL, NULL);
2222 return(STATUS_UNSUCCESSFUL);
2223 }
2224
2225 /*
2226 * Otherwise we have succeeded.
2227 */
2228 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2229 MmSetSavedSwapEntryPage(Page, 0);
2230 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
2231 Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2232 {
2233 MmLockSectionSegment(Context.Segment);
2234 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2235 MmUnlockSectionSegment(Context.Segment);
2236 }
2237 else
2238 {
2239 MmReleasePageMemoryConsumer(MC_USER, Page);
2240 }
2241
2242 if (Context.Private)
2243 {
2244 MmLockAddressSpace(AddressSpace);
2245 MmLockSectionSegment(Context.Segment);
2246 Status = MmCreatePageFileMapping(Process,
2247 Address,
2248 SwapEntry);
2249 /* We had placed a wait entry upon entry ... replace it before leaving */
2250 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2251 MmUnlockSectionSegment(Context.Segment);
2252 MmUnlockAddressSpace(AddressSpace);
2253 if (!NT_SUCCESS(Status))
2254 {
2255 DPRINT1("Status %x Creating page file mapping for %p:%p\n", Status, Process, Address);
2256 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2257 }
2258 }
2259 else
2260 {
2261 MmLockAddressSpace(AddressSpace);
2262 MmLockSectionSegment(Context.Segment);
2263 Entry = MAKE_SWAP_SSE(SwapEntry);
2264 /* We had placed a wait entry upon entry ... replace it before leaving */
2265 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2266 MmUnlockSectionSegment(Context.Segment);
2267 MmUnlockAddressSpace(AddressSpace);
2268 }
2269
2270 MiSetPageEvent(NULL, NULL);
2271 return(STATUS_SUCCESS);
2272 }
2273
2274 NTSTATUS
2275 NTAPI
2276 MmWritePageSectionView(PMMSUPPORT AddressSpace,
2277 PMEMORY_AREA MemoryArea,
2278 PVOID Address,
2279 ULONG PageEntry)
2280 {
2281 LARGE_INTEGER Offset;
2282 PROS_SECTION_OBJECT Section;
2283 PMM_SECTION_SEGMENT Segment;
2284 PFN_NUMBER Page;
2285 SWAPENTRY SwapEntry;
2286 ULONG_PTR Entry;
2287 BOOLEAN Private;
2288 NTSTATUS Status;
2289 PFILE_OBJECT FileObject;
2290 #ifndef NEWCC
2291 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
2292 #endif
2293 BOOLEAN DirectMapped;
2294 BOOLEAN IsImageSection;
2295 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2296
2297 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2298
2299 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
2300 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2301
2302 /*
2303 * Get the segment and section.
2304 */
2305 Segment = MemoryArea->Data.SectionData.Segment;
2306 Section = MemoryArea->Data.SectionData.Section;
2307 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2308
2309 FileObject = Section->FileObject;
2310 DirectMapped = FALSE;
2311 if (FileObject != NULL &&
2312 !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2313 {
2314 #ifndef NEWCC
2315 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
2316 #endif
2317
2318 /*
2319 * If the file system is letting us go directly to the cache and the
2320 * memory area was mapped at an offset in the file which is page aligned
2321 * then note this is a direct mapped page.
2322 */
2323 if (((Offset.QuadPart + Segment->Image.FileOffset) % PAGE_SIZE) == 0 &&
2324 (Offset.QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
2325 {
2326 DirectMapped = TRUE;
2327 }
2328 }
2329
2330 /*
2331 * This should never happen since mappings of physical memory are never
2332 * placed in the rmap lists.
2333 */
2334 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2335 {
2336 DPRINT1("Trying to write back page from physical memory mapped at %p "
2337 "process %p\n", Address,
2338 Process ? Process->UniqueProcessId : 0);
2339 KeBugCheck(MEMORY_MANAGEMENT);
2340 }
2341
2342 /*
2343 * Get the section segment entry and the physical address.
2344 */
2345 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2346 if (!MmIsPagePresent(Process, Address))
2347 {
2348 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
2349 Process ? Process->UniqueProcessId : 0, Address);
2350 KeBugCheck(MEMORY_MANAGEMENT);
2351 }
2352 Page = MmGetPfnForProcess(Process, Address);
2353 SwapEntry = MmGetSavedSwapEntryPage(Page);
2354
2355 /*
2356 * Check for a private (COWed) page.
2357 */
2358 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2359 IS_SWAP_FROM_SSE(Entry) ||
2360 PFN_FROM_SSE(Entry) != Page)
2361 {
2362 Private = TRUE;
2363 }
2364 else
2365 {
2366 Private = FALSE;
2367 }
2368
2369 /*
2370 * Speculatively set all mappings of the page to clean.
2371 */
2372 MmSetCleanAllRmaps(Page);
2373
2374 /*
2375 * If this page was direct mapped from the cache then the cache manager
2376 * will take care of writing it back to disk.
2377 */
2378 if (DirectMapped && !Private)
2379 {
2380 //LARGE_INTEGER SOffset;
2381 ASSERT(SwapEntry == 0);
2382 //SOffset.QuadPart = Offset.QuadPart + Segment->Image.FileOffset;
2383 #ifndef NEWCC
2384 CcRosMarkDirtyVacb(SharedCacheMap, Offset.QuadPart);
2385 #endif
2386 MmLockSectionSegment(Segment);
2387 MmSetPageEntrySectionSegment(Segment, &Offset, PageEntry);
2388 MmUnlockSectionSegment(Segment);
2389 MiSetPageEvent(NULL, NULL);
2390 return(STATUS_SUCCESS);
2391 }
2392
2393 /*
2394 * If necessary, allocate an entry in the paging file for this page
2395 */
2396 if (SwapEntry == 0)
2397 {
2398 SwapEntry = MmAllocSwapPage();
2399 if (SwapEntry == 0)
2400 {
2401 MmSetDirtyAllRmaps(Page);
2402 MiSetPageEvent(NULL, NULL);
2403 return(STATUS_PAGEFILE_QUOTA);
2404 }
2405 MmSetSavedSwapEntryPage(Page, SwapEntry);
2406 }
2407
2408 /*
2409 * Write the page to the pagefile
2410 */
2411 Status = MmWriteToSwapPage(SwapEntry, Page);
2412 if (!NT_SUCCESS(Status))
2413 {
2414 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2415 Status);
2416 MmSetDirtyAllRmaps(Page);
2417 MiSetPageEvent(NULL, NULL);
2418 return(STATUS_UNSUCCESSFUL);
2419 }
2420
2421 /*
2422 * Otherwise we have succeeded.
2423 */
2424 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2425 MiSetPageEvent(NULL, NULL);
2426 return(STATUS_SUCCESS);
2427 }
2428
2429 static VOID
2430 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
2431 PVOID BaseAddress,
2432 SIZE_T RegionSize,
2433 ULONG OldType,
2434 ULONG OldProtect,
2435 ULONG NewType,
2436 ULONG NewProtect)
2437 {
2438 PMEMORY_AREA MemoryArea;
2439 PMM_SECTION_SEGMENT Segment;
2440 BOOLEAN DoCOW = FALSE;
2441 ULONG i;
2442 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2443
2444 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
2445 ASSERT(MemoryArea != NULL);
2446 Segment = MemoryArea->Data.SectionData.Segment;
2447 MmLockSectionSegment(Segment);
2448
2449 if ((Segment->WriteCopy) &&
2450 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
2451 {
2452 DoCOW = TRUE;
2453 }
2454
2455 if (OldProtect != NewProtect)
2456 {
2457 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
2458 {
2459 SWAPENTRY SwapEntry;
2460 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
2461 ULONG Protect = NewProtect;
2462
2463 /* Wait for a wait entry to disappear */
2464 do
2465 {
2466 MmGetPageFileMapping(Process, Address, &SwapEntry);
2467 if (SwapEntry != MM_WAIT_ENTRY)
2468 break;
2469 MiWaitForPageEvent(Process, Address);
2470 }
2471 while (TRUE);
2472
2473 /*
2474 * If we doing COW for this segment then check if the page is
2475 * already private.
2476 */
2477 if (DoCOW && MmIsPagePresent(Process, Address))
2478 {
2479 LARGE_INTEGER Offset;
2480 ULONG_PTR Entry;
2481 PFN_NUMBER Page;
2482
2483 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
2484 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2485 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2486 /*
2487 * An MM_WAIT_ENTRY is ok in this case... It'll just count as
2488 * IS_SWAP_FROM_SSE and we'll do the right thing.
2489 */
2490 Page = MmGetPfnForProcess(Process, Address);
2491
2492 Protect = PAGE_READONLY;
2493 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2494 IS_SWAP_FROM_SSE(Entry) ||
2495 PFN_FROM_SSE(Entry) != Page)
2496 {
2497 Protect = NewProtect;
2498 }
2499 }
2500
2501 if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
2502 {
2503 MmSetPageProtect(Process, Address,
2504 Protect);
2505 }
2506 }
2507 }
2508
2509 MmUnlockSectionSegment(Segment);
2510 }
2511
2512 NTSTATUS
2513 NTAPI
2514 MmProtectSectionView(PMMSUPPORT AddressSpace,
2515 PMEMORY_AREA MemoryArea,
2516 PVOID BaseAddress,
2517 SIZE_T Length,
2518 ULONG Protect,
2519 PULONG OldProtect)
2520 {
2521 PMM_REGION Region;
2522 NTSTATUS Status;
2523 ULONG_PTR MaxLength;
2524
2525 MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2526 if (Length > MaxLength)
2527 Length = (ULONG)MaxLength;
2528
2529 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2530 &MemoryArea->Data.SectionData.RegionListHead,
2531 BaseAddress, NULL);
2532 ASSERT(Region != NULL);
2533
2534 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2535 Region->Protect != Protect)
2536 {
2537 return STATUS_INVALID_PAGE_PROTECTION;
2538 }
2539
2540 *OldProtect = Region->Protect;
2541 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2542 &MemoryArea->Data.SectionData.RegionListHead,
2543 BaseAddress, Length, Region->Type, Protect,
2544 MmAlterViewAttributes);
2545
2546 return(Status);
2547 }
2548
2549 NTSTATUS NTAPI
2550 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2551 PVOID Address,
2552 PMEMORY_BASIC_INFORMATION Info,
2553 PSIZE_T ResultLength)
2554 {
2555 PMM_REGION Region;
2556 PVOID RegionBaseAddress;
2557 PROS_SECTION_OBJECT Section;
2558 PMM_SECTION_SEGMENT Segment;
2559
2560 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2561 &MemoryArea->Data.SectionData.RegionListHead,
2562 Address, &RegionBaseAddress);
2563 if (Region == NULL)
2564 {
2565 return STATUS_UNSUCCESSFUL;
2566 }
2567
2568 Section = MemoryArea->Data.SectionData.Section;
2569 if (Section->AllocationAttributes & SEC_IMAGE)
2570 {
2571 Segment = MemoryArea->Data.SectionData.Segment;
2572 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2573 Info->Type = MEM_IMAGE;
2574 }
2575 else
2576 {
2577 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2578 Info->Type = MEM_MAPPED;
2579 }
2580 Info->BaseAddress = RegionBaseAddress;
2581 Info->AllocationProtect = MemoryArea->Protect;
2582 Info->RegionSize = Region->Length;
2583 Info->State = MEM_COMMIT;
2584 Info->Protect = Region->Protect;
2585
2586 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2587 return(STATUS_SUCCESS);
2588 }
2589
2590 VOID
2591 NTAPI
2592 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2593 {
2594 ULONG Length;
2595 LARGE_INTEGER Offset;
2596 ULONG_PTR Entry;
2597 SWAPENTRY SavedSwapEntry;
2598 PFN_NUMBER Page;
2599
2600 Page = 0;
2601
2602 MmLockSectionSegment(Segment);
2603
2604 Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
2605 for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
2606 {
2607 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2608 if (Entry)
2609 {
2610 MmSetPageEntrySectionSegment(Segment, &Offset, 0);
2611 if (IS_SWAP_FROM_SSE(Entry))
2612 {
2613 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2614 }
2615 else
2616 {
2617 Page = PFN_FROM_SSE(Entry);
2618 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2619 if (SavedSwapEntry != 0)
2620 {
2621 MmSetSavedSwapEntryPage(Page, 0);
2622 MmFreeSwapPage(SavedSwapEntry);
2623 }
2624 MmReleasePageMemoryConsumer(MC_USER, Page);
2625 }
2626 }
2627 }
2628
2629 MmUnlockSectionSegment(Segment);
2630 }
2631
2632 VOID NTAPI
2633 MmpDeleteSection(PVOID ObjectBody)
2634 {
2635 PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
2636
2637 /* Check if it's an ARM3, or ReactOS section */
2638 if (!MiIsRosSectionObject(Section))
2639 {
2640 MiDeleteARM3Section(ObjectBody);
2641 return;
2642 }
2643
2644 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2645 if (Section->AllocationAttributes & SEC_IMAGE)
2646 {
2647 ULONG i;
2648 ULONG NrSegments;
2649 ULONG RefCount;
2650 PMM_SECTION_SEGMENT SectionSegments;
2651
2652 /*
2653 * NOTE: Section->ImageSection can be NULL for short time
2654 * during the section creating. If we fail for some reason
2655 * until the image section is properly initialized we shouldn't
2656 * process further here.
2657 */
2658 if (Section->ImageSection == NULL)
2659 return;
2660
2661 SectionSegments = Section->ImageSection->Segments;
2662 NrSegments = Section->ImageSection->NrSegments;
2663
2664 for (i = 0; i < NrSegments; i++)
2665 {
2666 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2667 {
2668 MmLockSectionSegment(&SectionSegments[i]);
2669 }
2670 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2671 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2672 {
2673 MmUnlockSectionSegment(&SectionSegments[i]);
2674 if (RefCount == 0)
2675 {
2676 MmpFreePageFileSegment(&SectionSegments[i]);
2677 }
2678 }
2679 }
2680 }
2681 #ifdef NEWCC
2682 else if (Section->Segment && Section->Segment->Flags & MM_DATAFILE_SEGMENT)
2683 {
2684 ULONG RefCount = 0;
2685 PMM_SECTION_SEGMENT Segment = Section->Segment;
2686
2687 if (Segment &&
2688 (RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
2689 {
2690 DPRINT("Freeing section segment\n");
2691 Section->Segment = NULL;
2692 MmFinalizeSegment(Segment);
2693 }
2694 else
2695 {
2696 DPRINT("RefCount %d\n", RefCount);
2697 }
2698 }
2699 #endif
2700 else
2701 {
2702 /*
2703 * NOTE: Section->Segment can be NULL for short time
2704 * during the section creating.
2705 */
2706 if (Section->Segment == NULL)
2707 return;
2708
2709 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2710 {
2711 MmpFreePageFileSegment(Section->Segment);
2712 MmFreePageTablesSectionSegment(Section->Segment, NULL);
2713 ExFreePool(Section->Segment);
2714 Section->Segment = NULL;
2715 }
2716 else
2717 {
2718 (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2719 }
2720 }
2721 if (Section->FileObject != NULL)
2722 {
2723 #ifndef NEWCC
2724 CcRosDereferenceCache(Section->FileObject);
2725 #endif
2726 ObDereferenceObject(Section->FileObject);
2727 Section->FileObject = NULL;
2728 }
2729 }
2730
2731 VOID NTAPI
2732 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2733 IN PVOID Object,
2734 IN ACCESS_MASK GrantedAccess,
2735 IN ULONG ProcessHandleCount,
2736 IN ULONG SystemHandleCount)
2737 {
2738 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2739 }
2740
2741 NTSTATUS
2742 INIT_FUNCTION
2743 NTAPI
2744 MmCreatePhysicalMemorySection(VOID)
2745 {
2746 PROS_SECTION_OBJECT PhysSection;
2747 NTSTATUS Status;
2748 OBJECT_ATTRIBUTES Obj;
2749 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2750 LARGE_INTEGER SectionSize;
2751 HANDLE Handle;
2752
2753 /*
2754 * Create the section mapping physical memory
2755 */
2756 SectionSize.QuadPart = 0xFFFFFFFF;
2757 InitializeObjectAttributes(&Obj,
2758 &Name,
2759 OBJ_PERMANENT,
2760 NULL,
2761 NULL);
2762 Status = MmCreateSection((PVOID)&PhysSection,
2763 SECTION_ALL_ACCESS,
2764 &Obj,
2765 &SectionSize,
2766 PAGE_EXECUTE_READWRITE,
2767 SEC_PHYSICALMEMORY,
2768 NULL,
2769 NULL);
2770 if (!NT_SUCCESS(Status))
2771 {
2772 DPRINT1("Failed to create PhysicalMemory section\n");
2773 KeBugCheck(MEMORY_MANAGEMENT);
2774 }
2775 Status = ObInsertObject(PhysSection,
2776 NULL,
2777 SECTION_ALL_ACCESS,
2778 0,
2779 NULL,
2780 &Handle);
2781 if (!NT_SUCCESS(Status))
2782 {
2783 ObDereferenceObject(PhysSection);
2784 }
2785 ObCloseHandle(Handle, KernelMode);
2786 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2787 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2788
2789 return(STATUS_SUCCESS);
2790 }
2791
2792 NTSTATUS
2793 INIT_FUNCTION
2794 NTAPI
2795 MmInitSectionImplementation(VOID)
2796 {
2797 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2798 UNICODE_STRING Name;
2799
2800 DPRINT("Creating Section Object Type\n");
2801
2802 /* Initialize the section based root */
2803 ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2804 MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2805
2806 /* Initialize the Section object type */
2807 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2808 RtlInitUnicodeString(&Name, L"Section");
2809 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2810 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT);
2811 ObjectTypeInitializer.PoolType = PagedPool;
2812 ObjectTypeInitializer.UseDefaultObject = TRUE;
2813 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2814 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2815 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2816 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2817 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2818 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2819
2820 MmCreatePhysicalMemorySection();
2821
2822 return(STATUS_SUCCESS);
2823 }
2824
2825 NTSTATUS
2826 NTAPI
2827 MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
2828 ACCESS_MASK DesiredAccess,
2829 POBJECT_ATTRIBUTES ObjectAttributes,
2830 PLARGE_INTEGER UMaximumSize,
2831 ULONG SectionPageProtection,
2832 ULONG AllocationAttributes)
2833 /*
2834 * Create a section which is backed by the pagefile
2835 */
2836 {
2837 LARGE_INTEGER MaximumSize;
2838 PROS_SECTION_OBJECT Section;
2839 PMM_SECTION_SEGMENT Segment;
2840 NTSTATUS Status;
2841
2842 if (UMaximumSize == NULL)
2843 {
2844 DPRINT1("MmCreatePageFileSection: (UMaximumSize == NULL)\n");
2845 return(STATUS_INVALID_PARAMETER);
2846 }
2847 MaximumSize = *UMaximumSize;
2848
2849 /*
2850 * Create the section
2851 */
2852 Status = ObCreateObject(ExGetPreviousMode(),
2853 MmSectionObjectType,
2854 ObjectAttributes,
2855 ExGetPreviousMode(),
2856 NULL,
2857 sizeof(ROS_SECTION_OBJECT),
2858 0,
2859 0,
2860 (PVOID*)(PVOID)&Section);
2861 if (!NT_SUCCESS(Status))
2862 {
2863 DPRINT1("MmCreatePageFileSection: failed to create object (0x%lx)\n", Status);
2864 return(Status);
2865 }
2866
2867 /*
2868 * Initialize it
2869 */
2870 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2871 Section->Type = 'SC';
2872 Section->Size = 'TN';
2873 Section->SectionPageProtection = SectionPageProtection;
2874 Section->AllocationAttributes = AllocationAttributes;
2875 Section->MaximumSize = MaximumSize;
2876 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2877 TAG_MM_SECTION_SEGMENT);
2878 if (Segment == NULL)
2879 {
2880 ObDereferenceObject(Section);
2881 return(STATUS_NO_MEMORY);
2882 }
2883 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2884 Section->Segment = Segment;
2885 Segment->ReferenceCount = 1;
2886 ExInitializeFastMutex(&Segment->Lock);
2887 Segment->Image.FileOffset = 0;
2888 Segment->Protection = SectionPageProtection;
2889 Segment->RawLength.QuadPart = MaximumSize.u.LowPart;
2890 Segment->Length.QuadPart = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2891 Segment->Flags = MM_PAGEFILE_SEGMENT;
2892 Segment->WriteCopy = FALSE;
2893 Segment->Image.VirtualAddress = 0;
2894 Segment->Image.Characteristics = 0;
2895 *SectionObject = Section;
2896 MiInitializeSectionPageTable(Segment);
2897 return(STATUS_SUCCESS);
2898 }
2899
2900 NTSTATUS
2901 NTAPI
2902 MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject,
2903 ACCESS_MASK DesiredAccess,
2904 POBJECT_ATTRIBUTES ObjectAttributes,
2905 PLARGE_INTEGER UMaximumSize,
2906 ULONG SectionPageProtection,
2907 ULONG AllocationAttributes,
2908 HANDLE FileHandle)
2909 /*
2910 * Create a section backed by a data file
2911 */
2912 {
2913 PROS_SECTION_OBJECT Section;
2914 NTSTATUS Status;
2915 LARGE_INTEGER MaximumSize;
2916 PFILE_OBJECT FileObject;
2917 PMM_SECTION_SEGMENT Segment;
2918 ULONG FileAccess;
2919 IO_STATUS_BLOCK Iosb;
2920 LARGE_INTEGER Offset;
2921 CHAR Buffer;
2922 FILE_STANDARD_INFORMATION FileInfo;
2923 ULONG Length;
2924
2925 /*
2926 * Create the section
2927 */
2928 Status = ObCreateObject(ExGetPreviousMode(),
2929 MmSectionObjectType,
2930 ObjectAttributes,
2931 ExGetPreviousMode(),
2932 NULL,
2933 sizeof(ROS_SECTION_OBJECT),
2934 0,
2935 0,
2936 (PVOID*)&Section);
2937 if (!NT_SUCCESS(Status))
2938 {
2939 return(Status);
2940 }
2941 /*
2942 * Initialize it
2943 */
2944 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2945 Section->Type = 'SC';
2946 Section->Size = 'TN';
2947 Section->SectionPageProtection = SectionPageProtection;
2948 Section->AllocationAttributes = AllocationAttributes;
2949
2950 /*
2951 * Reference the file handle
2952 */
2953 FileAccess = MiArm3GetCorrectFileAccessMask(SectionPageProtection);
2954 Status = ObReferenceObjectByHandle(FileHandle,
2955 FileAccess,
2956 IoFileObjectType,
2957 ExGetPreviousMode(),
2958 (PVOID*)(PVOID)&FileObject,
2959 NULL);
2960 if (!NT_SUCCESS(Status))
2961 {
2962 ObDereferenceObject(Section);
2963 return(Status);
2964 }
2965
2966 /*
2967 * FIXME: This is propably not entirely correct. We can't look into
2968 * the standard FCB header because it might not be initialized yet
2969 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2970 * standard file information is filled on first request).
2971 */
2972 Status = IoQueryFileInformation(FileObject,
2973 FileStandardInformation,
2974 sizeof(FILE_STANDARD_INFORMATION),
2975 &FileInfo,
2976 &Length);
2977 Iosb.Information = Length;
2978 if (!NT_SUCCESS(Status))
2979 {
2980 ObDereferenceObject(Section);
2981 ObDereferenceObject(FileObject);
2982 return Status;
2983 }
2984
2985 /*
2986 * FIXME: Revise this once a locking order for file size changes is
2987 * decided
2988 */
2989 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2990 {
2991 MaximumSize = *UMaximumSize;
2992 }
2993 else
2994 {
2995 MaximumSize = FileInfo.EndOfFile;
2996 /* Mapping zero-sized files isn't allowed. */
2997 if (MaximumSize.QuadPart == 0)
2998 {
2999 ObDereferenceObject(Section);
3000 ObDereferenceObject(FileObject);
3001 return STATUS_FILE_INVALID;
3002 }
3003 }
3004
3005 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
3006 {
3007 Status = IoSetInformation(FileObject,
3008 FileAllocationInformation,
3009 sizeof(LARGE_INTEGER),
3010 &MaximumSize);
3011 if (!NT_SUCCESS(Status))
3012 {
3013 ObDereferenceObject(Section);
3014 ObDereferenceObject(FileObject);
3015 return(STATUS_SECTION_NOT_EXTENDED);
3016 }
3017 }
3018
3019 if (FileObject->SectionObjectPointer == NULL ||
3020 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3021 {
3022 /*
3023 * Read a bit so caching is initiated for the file object.
3024 * This is only needed because MiReadPage currently cannot
3025 * handle non-cached streams.
3026 */
3027 Offset.QuadPart = 0;
3028 Status = ZwReadFile(FileHandle,
3029 NULL,
3030 NULL,
3031 NULL,
3032 &Iosb,
3033 &Buffer,
3034 sizeof (Buffer),
3035 &Offset,
3036 0);
3037 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
3038 {
3039 ObDereferenceObject(Section);
3040 ObDereferenceObject(FileObject);
3041 return(Status);
3042 }
3043 if (FileObject->SectionObjectPointer == NULL ||
3044 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3045 {
3046 /* FIXME: handle this situation */
3047 ObDereferenceObject(Section);
3048 ObDereferenceObject(FileObject);
3049 return STATUS_INVALID_PARAMETER;
3050 }
3051 }
3052
3053 /*
3054 * Lock the file
3055 */
3056 Status = MmspWaitForFileLock(FileObject);
3057 if (Status != STATUS_SUCCESS)
3058 {
3059 ObDereferenceObject(Section);
3060 ObDereferenceObject(FileObject);
3061 return(Status);
3062 }
3063
3064 /*
3065 * If this file hasn't been mapped as a data file before then allocate a
3066 * section segment to describe the data file mapping
3067 */
3068 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
3069 {
3070 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
3071 TAG_MM_SECTION_SEGMENT);
3072 if (Segment == NULL)
3073 {
3074 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3075 ObDereferenceObject(Section);
3076 ObDereferenceObject(FileObject);
3077 return(STATUS_NO_MEMORY);
3078 }
3079 Section->Segment = Segment;
3080 Segment->ReferenceCount = 1;
3081 ExInitializeFastMutex(&Segment->Lock);
3082 /*
3083 * Set the lock before assigning the segment to the file object
3084 */
3085 ExAcquireFastMutex(&Segment->Lock);
3086 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
3087
3088 Segment->Image.FileOffset = 0;
3089 Segment->Protection = SectionPageProtection;
3090 Segment->Flags = MM_DATAFILE_SEGMENT;
3091 Segment->Image.Characteristics = 0;
3092 Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
3093 if (AllocationAttributes & SEC_RESERVE)
3094 {
3095 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
3096 }
3097 else
3098 {
3099 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3100 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3101 }
3102 Segment->Image.VirtualAddress = 0;
3103 Segment->Locked = TRUE;
3104 MiInitializeSectionPageTable(Segment);
3105 }
3106 else
3107 {
3108 /*
3109 * If the file is already mapped as a data file then we may need
3110 * to extend it
3111 */
3112 Segment =
3113 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
3114 DataSectionObject;
3115 Section->Segment = Segment;
3116 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
3117 MmLockSectionSegment(Segment);
3118
3119 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
3120 !(AllocationAttributes & SEC_RESERVE))
3121 {
3122 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3123 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3124 }
3125 }
3126 MmUnlockSectionSegment(Segment);
3127 Section->FileObject = FileObject;
3128 Section->MaximumSize = MaximumSize;
3129 #ifndef NEWCC
3130 CcRosReferenceCache(FileObject);
3131 #endif
3132 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3133 *SectionObject = Section;
3134 return(STATUS_SUCCESS);
3135 }
3136
3137 /*
3138 TODO: not that great (declaring loaders statically, having to declare all of
3139 them, having to keep them extern, etc.), will fix in the future
3140 */
3141 extern NTSTATUS NTAPI PeFmtCreateSection
3142 (
3143 IN CONST VOID * FileHeader,
3144 IN SIZE_T FileHeaderSize,
3145 IN PVOID File,
3146 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3147 OUT PULONG Flags,
3148 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3149 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3150 );
3151
3152 extern NTSTATUS NTAPI ElfFmtCreateSection
3153 (
3154 IN CONST VOID * FileHeader,
3155 IN SIZE_T FileHeaderSize,
3156 IN PVOID File,
3157 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3158 OUT PULONG Flags,
3159 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3160 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3161 );
3162
3163 /* TODO: this is a standard DDK/PSDK macro */
3164 #ifndef RTL_NUMBER_OF
3165 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
3166 #endif
3167
3168 static PEXEFMT_LOADER ExeFmtpLoaders[] =
3169 {
3170 PeFmtCreateSection,
3171 #ifdef __ELF
3172 ElfFmtCreateSection
3173 #endif
3174 };
3175
3176 static
3177 PMM_SECTION_SEGMENT
3178 NTAPI
3179 ExeFmtpAllocateSegments(IN ULONG NrSegments)
3180 {
3181 SIZE_T SizeOfSegments;
3182 PMM_SECTION_SEGMENT Segments;
3183
3184 /* TODO: check for integer overflow */
3185 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
3186
3187 Segments = ExAllocatePoolWithTag(NonPagedPool,
3188 SizeOfSegments,
3189 TAG_MM_SECTION_SEGMENT);
3190
3191 if(Segments)
3192 RtlZeroMemory(Segments, SizeOfSegments);
3193
3194 return Segments;
3195 }
3196
3197 static
3198 NTSTATUS
3199 NTAPI
3200 ExeFmtpReadFile(IN PVOID File,
3201 IN PLARGE_INTEGER Offset,
3202 IN ULONG Length,
3203 OUT PVOID * Data,
3204 OUT PVOID * AllocBase,
3205 OUT PULONG ReadSize)
3206 {
3207 NTSTATUS Status;
3208 LARGE_INTEGER FileOffset;
3209 ULONG AdjustOffset;
3210 ULONG OffsetAdjustment;
3211 ULONG BufferSize;
3212 ULONG UsedSize;
3213 PVOID Buffer;
3214 PFILE_OBJECT FileObject = File;
3215 IO_STATUS_BLOCK Iosb;
3216
3217 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
3218
3219 if(Length == 0)
3220 {
3221 KeBugCheck(MEMORY_MANAGEMENT);
3222 }
3223
3224 FileOffset = *Offset;
3225
3226 /* Negative/special offset: it cannot be used in this context */
3227 if(FileOffset.u.HighPart < 0)
3228 {
3229 KeBugCheck(MEMORY_MANAGEMENT);
3230 }
3231
3232 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
3233 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
3234 FileOffset.u.LowPart = AdjustOffset;
3235
3236 BufferSize = Length + OffsetAdjustment;
3237 BufferSize = PAGE_ROUND_UP(BufferSize);
3238
3239 /* Flush data since we're about to perform a non-cached read */
3240 CcFlushCache(FileObject->SectionObjectPointer,
3241 &FileOffset,
3242 BufferSize,
3243 &Iosb);
3244
3245 /*
3246 * It's ok to use paged pool, because this is a temporary buffer only used in
3247 * the loading of executables. The assumption is that MmCreateSection is
3248 * always called at low IRQLs and that these buffers don't survive a brief
3249 * initialization phase
3250 */
3251 Buffer = ExAllocatePoolWithTag(PagedPool,
3252 BufferSize,
3253 'rXmM');
3254 if (!Buffer)
3255 {
3256 return STATUS_INSUFFICIENT_RESOURCES;
3257 }
3258
3259 UsedSize = 0;
3260
3261 Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
3262
3263 UsedSize = (ULONG)Iosb.Information;
3264
3265 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
3266 {
3267 Status = STATUS_IN_PAGE_ERROR;
3268 ASSERT(!NT_SUCCESS(Status));
3269 }
3270
3271 if(NT_SUCCESS(Status))
3272 {
3273 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
3274 *AllocBase = Buffer;
3275 *ReadSize = UsedSize - OffsetAdjustment;
3276 }
3277 else