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