7ece609b11000879cf2cb5a65cf82d2e5959efba
[reactos.git] / reactos / ntoskrnl / mm / section.c
1 /*
2 * Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 *
19 * PROJECT: ReactOS kernel
20 * FILE: ntoskrnl/mm/section.c
21 * PURPOSE: Implements section objects
22 *
23 * PROGRAMMERS: Rex Jolliff
24 * David Welch
25 * Eric Kohl
26 * Emanuele Aliberti
27 * Eugene Ingerman
28 * Casper Hornstrup
29 * KJK::Hyperion
30 * Guido de Jong
31 * Ge van Geldorp
32 * Royce Mitchell III
33 * Filip Navara
34 * Aleksey Bragin
35 * Jason Filby
36 * Thomas Weidenmueller
37 * Gunnar Andre' Dalsnes
38 * Mike Nordell
39 * Alex Ionescu
40 * Gregor Anich
41 * Steven Edwards
42 * Herve Poussineau
43 */
44
45 /* INCLUDES *****************************************************************/
46
47 #include <ntoskrnl.h>
48 #include <cache/newcc.h>
49 #include <cache/section/newmm.h>
50 #define NDEBUG
51 #include <debug.h>
52 #include <reactos/exeformat.h>
53
54 #if defined (ALLOC_PRAGMA)
55 #pragma alloc_text(INIT, MmCreatePhysicalMemorySection)
56 #pragma alloc_text(INIT, MmInitSectionImplementation)
57 #endif
58
59 #include "ARM3/miarm.h"
60
61 #undef MmSetPageEntrySectionSegment
62 #define MmSetPageEntrySectionSegment(S,O,E) do { \
63 DPRINT("SetPageEntrySectionSegment(old,%p,%x,%x)\n",(S),(O)->LowPart,E); \
64 _MmSetPageEntrySectionSegment((S),(O),(E),__FILE__,__LINE__); \
65 } while (0)
66
67 extern MMSESSION MmSession;
68
69 NTSTATUS
70 NTAPI
71 MiMapViewInSystemSpace(IN PVOID Section,
72 IN PVOID Session,
73 OUT PVOID *MappedBase,
74 IN OUT PSIZE_T ViewSize);
75
76 NTSTATUS
77 NTAPI
78 MmCreateArm3Section(OUT PVOID *SectionObject,
79 IN ACCESS_MASK DesiredAccess,
80 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
81 IN PLARGE_INTEGER InputMaximumSize,
82 IN ULONG SectionPageProtection,
83 IN ULONG AllocationAttributes,
84 IN HANDLE FileHandle OPTIONAL,
85 IN PFILE_OBJECT FileObject OPTIONAL);
86
87 NTSTATUS
88 NTAPI
89 MmMapViewOfArm3Section(IN PVOID SectionObject,
90 IN PEPROCESS Process,
91 IN OUT PVOID *BaseAddress,
92 IN ULONG_PTR ZeroBits,
93 IN SIZE_T CommitSize,
94 IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
95 IN OUT PSIZE_T ViewSize,
96 IN SECTION_INHERIT InheritDisposition,
97 IN ULONG AllocationType,
98 IN ULONG Protect);
99
100 //
101 // PeFmtCreateSection depends on the following:
102 //
103 C_ASSERT(EXEFMT_LOAD_HEADER_SIZE >= sizeof(IMAGE_DOS_HEADER));
104 C_ASSERT(sizeof(IMAGE_NT_HEADERS32) <= sizeof(IMAGE_NT_HEADERS64));
105
106 C_ASSERT(TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == TYPE_ALIGNMENT(IMAGE_NT_HEADERS64));
107 C_ASSERT(RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader) == RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS64, FileHeader));
108 C_ASSERT(FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader) == FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader));
109
110 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Magic));
111 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SectionAlignment));
112 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, FileAlignment));
113 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, Subsystem));
114 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MinorSubsystemVersion));
115 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, MajorSubsystemVersion));
116 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, AddressOfEntryPoint));
117 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfCode));
118 C_ASSERT(PEFMT_FIELDS_EQUAL(IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, SizeOfHeaders));
119
120 /* TYPES *********************************************************************/
121
122 typedef struct
123 {
124 PROS_SECTION_OBJECT Section;
125 PMM_SECTION_SEGMENT Segment;
126 LARGE_INTEGER Offset;
127 BOOLEAN WasDirty;
128 BOOLEAN Private;
129 PEPROCESS CallingProcess;
130 ULONG_PTR SectionEntry;
131 }
132 MM_SECTION_PAGEOUT_CONTEXT;
133
134 /* GLOBALS *******************************************************************/
135
136 POBJECT_TYPE MmSectionObjectType = NULL;
137
138 ULONG_PTR MmSubsectionBase;
139
140 static ULONG SectionCharacteristicsToProtect[16] =
141 {
142 PAGE_NOACCESS, /* 0 = NONE */
143 PAGE_NOACCESS, /* 1 = SHARED */
144 PAGE_EXECUTE, /* 2 = EXECUTABLE */
145 PAGE_EXECUTE, /* 3 = EXECUTABLE, SHARED */
146 PAGE_READONLY, /* 4 = READABLE */
147 PAGE_READONLY, /* 5 = READABLE, SHARED */
148 PAGE_EXECUTE_READ, /* 6 = READABLE, EXECUTABLE */
149 PAGE_EXECUTE_READ, /* 7 = READABLE, EXECUTABLE, SHARED */
150 /*
151 * FIXME? do we really need the WriteCopy field in segments? can't we use
152 * PAGE_WRITECOPY here?
153 */
154 PAGE_READWRITE, /* 8 = WRITABLE */
155 PAGE_READWRITE, /* 9 = WRITABLE, SHARED */
156 PAGE_EXECUTE_READWRITE, /* 10 = WRITABLE, EXECUTABLE */
157 PAGE_EXECUTE_READWRITE, /* 11 = WRITABLE, EXECUTABLE, SHARED */
158 PAGE_READWRITE, /* 12 = WRITABLE, READABLE */
159 PAGE_READWRITE, /* 13 = WRITABLE, READABLE, SHARED */
160 PAGE_EXECUTE_READWRITE, /* 14 = WRITABLE, READABLE, EXECUTABLE */
161 PAGE_EXECUTE_READWRITE, /* 15 = WRITABLE, READABLE, EXECUTABLE, SHARED */
162 };
163
164 extern ULONG MmMakeFileAccess [];
165 ACCESS_MASK NTAPI MiArm3GetCorrectFileAccessMask(IN ACCESS_MASK SectionPageProtection);
166 static GENERIC_MAPPING MmpSectionMapping =
167 {
168 STANDARD_RIGHTS_READ | SECTION_MAP_READ | SECTION_QUERY,
169 STANDARD_RIGHTS_WRITE | SECTION_MAP_WRITE,
170 STANDARD_RIGHTS_EXECUTE | SECTION_MAP_EXECUTE,
171 SECTION_ALL_ACCESS
172 };
173
174
175 /* FUNCTIONS *****************************************************************/
176
177
178 /*
179 References:
180 [1] Microsoft Corporation, "Microsoft Portable Executable and Common Object
181 File Format Specification", revision 6.0 (February 1999)
182 */
183 NTSTATUS NTAPI PeFmtCreateSection(IN CONST VOID * FileHeader,
184 IN SIZE_T FileHeaderSize,
185 IN PVOID File,
186 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
187 OUT PULONG Flags,
188 IN PEXEFMT_CB_READ_FILE ReadFileCb,
189 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb)
190 {
191 NTSTATUS nStatus;
192 ULONG cbFileHeaderOffsetSize = 0;
193 ULONG cbSectionHeadersOffset = 0;
194 ULONG cbSectionHeadersSize;
195 ULONG cbSectionHeadersOffsetSize = 0;
196 ULONG cbOptHeaderSize;
197 ULONG cbHeadersSize = 0;
198 ULONG nSectionAlignment;
199 ULONG nFileAlignment;
200 ULONG_PTR ImageBase;
201 const IMAGE_DOS_HEADER * pidhDosHeader;
202 const IMAGE_NT_HEADERS32 * pinhNtHeader;
203 const IMAGE_OPTIONAL_HEADER32 * piohOptHeader;
204 const IMAGE_SECTION_HEADER * pishSectionHeaders;
205 PMM_SECTION_SEGMENT pssSegments;
206 LARGE_INTEGER lnOffset;
207 PVOID pBuffer;
208 SIZE_T nPrevVirtualEndOfSegment = 0;
209 ULONG nFileSizeOfHeaders = 0;
210 ULONG i;
211
212 ASSERT(FileHeader);
213 ASSERT(FileHeaderSize > 0);
214 ASSERT(File);
215 ASSERT(ImageSectionObject);
216 ASSERT(ReadFileCb);
217 ASSERT(AllocateSegmentsCb);
218
219 ASSERT(Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize));
220
221 ASSERT(((UINT_PTR)FileHeader % TYPE_ALIGNMENT(IMAGE_DOS_HEADER)) == 0);
222
223 #define DIE(ARGS_) { DPRINT ARGS_; goto l_Return; }
224
225 pBuffer = NULL;
226 pidhDosHeader = FileHeader;
227
228 /* DOS HEADER */
229 nStatus = STATUS_ROS_EXEFMT_UNKNOWN_FORMAT;
230
231 /* image too small to be an MZ executable */
232 if(FileHeaderSize < sizeof(IMAGE_DOS_HEADER))
233 DIE(("Too small to be an MZ executable, size is %lu\n", FileHeaderSize));
234
235 /* no MZ signature */
236 if(pidhDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
237 DIE(("No MZ signature found, e_magic is %hX\n", pidhDosHeader->e_magic));
238
239 /* NT HEADER */
240 nStatus = STATUS_INVALID_IMAGE_PROTECT;
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 if(!Intsafe_AddULong32(&cbFileHeaderOffsetSize, pidhDosHeader->e_lfanew, RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader)))
247 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
248
249 if(FileHeaderSize < cbFileHeaderOffsetSize)
250 pinhNtHeader = NULL;
251 else
252 {
253 /*
254 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
255 * and FileHeaderSize >= cbFileHeaderOffsetSize, so this holds true too
256 */
257 ASSERT(Intsafe_CanOffsetPointer(FileHeader, pidhDosHeader->e_lfanew));
258 pinhNtHeader = (PVOID)((UINT_PTR)FileHeader + pidhDosHeader->e_lfanew);
259 }
260
261 /*
262 * the buffer doesn't contain the NT file header, or the alignment is wrong: we
263 * need to read the header from the file
264 */
265 if(FileHeaderSize < cbFileHeaderOffsetSize ||
266 (UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
267 {
268 ULONG cbNtHeaderSize;
269 ULONG cbReadSize;
270 PVOID pData;
271
272 l_ReadHeaderFromFile:
273 cbNtHeaderSize = 0;
274 lnOffset.QuadPart = pidhDosHeader->e_lfanew;
275
276 /* read the header from the file */
277 nStatus = ReadFileCb(File, &lnOffset, sizeof(IMAGE_NT_HEADERS64), &pData, &pBuffer, &cbReadSize);
278
279 if(!NT_SUCCESS(nStatus))
280 {
281 NTSTATUS ReturnedStatus = nStatus;
282
283 /* If it attempted to read past the end of the file, it means e_lfanew is invalid */
284 if (ReturnedStatus == STATUS_END_OF_FILE) nStatus = STATUS_INVALID_IMAGE_PROTECT;
285
286 DIE(("ReadFile failed, status %08X\n", ReturnedStatus));
287 }
288
289 ASSERT(pData);
290 ASSERT(pBuffer);
291 ASSERT(cbReadSize > 0);
292
293 nStatus = STATUS_INVALID_IMAGE_FORMAT;
294
295 /* the buffer doesn't contain the file header */
296 if(cbReadSize < RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS32, FileHeader))
297 DIE(("The file doesn't contain the PE file header\n"));
298
299 pinhNtHeader = pData;
300
301 /* object still not aligned: copy it to the beginning of the buffer */
302 if((UINT_PTR)pinhNtHeader % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) != 0)
303 {
304 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_NT_HEADERS32) == 0);
305 RtlMoveMemory(pBuffer, pData, cbReadSize);
306 pinhNtHeader = pBuffer;
307 }
308
309 /* invalid NT header */
310 nStatus = STATUS_INVALID_IMAGE_PROTECT;
311
312 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
313 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
314
315 nStatus = STATUS_INVALID_IMAGE_FORMAT;
316
317 if(!Intsafe_AddULong32(&cbNtHeaderSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
318 DIE(("The full NT header is too large\n"));
319
320 /* the buffer doesn't contain the whole NT header */
321 if(cbReadSize < cbNtHeaderSize)
322 DIE(("The file doesn't contain the full NT header\n"));
323 }
324 else
325 {
326 ULONG cbOptHeaderOffsetSize = 0;
327
328 nStatus = STATUS_INVALID_IMAGE_PROTECT;
329
330 /* don't trust an invalid NT header */
331 if(pinhNtHeader->Signature != IMAGE_NT_SIGNATURE)
332 DIE(("The file isn't a PE executable, Signature is %X\n", pinhNtHeader->Signature));
333
334 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
335 DIE(("The DOS stub is too large, e_lfanew is %X\n", pidhDosHeader->e_lfanew));
336
337 nStatus = STATUS_INVALID_IMAGE_FORMAT;
338
339 if(!Intsafe_AddULong32(&cbOptHeaderOffsetSize, cbOptHeaderOffsetSize, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
340 DIE(("The NT header is too large, SizeOfOptionalHeader is %X\n", pinhNtHeader->FileHeader.SizeOfOptionalHeader));
341
342 /* the buffer doesn't contain the whole NT header: read it from the file */
343 if(cbOptHeaderOffsetSize > FileHeaderSize)
344 goto l_ReadHeaderFromFile;
345 }
346
347 /* read information from the NT header */
348 piohOptHeader = &pinhNtHeader->OptionalHeader;
349 cbOptHeaderSize = pinhNtHeader->FileHeader.SizeOfOptionalHeader;
350
351 nStatus = STATUS_INVALID_IMAGE_FORMAT;
352
353 if(!RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Magic))
354 DIE(("The optional header doesn't contain the Magic field, SizeOfOptionalHeader is %X\n", cbOptHeaderSize));
355
356 /* ASSUME: RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject)); */
357
358 switch(piohOptHeader->Magic)
359 {
360 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
361 #ifdef _WIN64
362 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
363 #endif // _WIN64
364 break;
365
366 default:
367 DIE(("Unrecognized optional header, Magic is %X\n", piohOptHeader->Magic));
368 }
369
370 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SectionAlignment) &&
371 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, FileAlignment))
372 {
373 /* See [1], section 3.4.2 */
374 if(piohOptHeader->SectionAlignment < PAGE_SIZE)
375 {
376 if(piohOptHeader->FileAlignment != piohOptHeader->SectionAlignment)
377 DIE(("Sections aren't page-aligned and the file alignment isn't the same\n"));
378 }
379 else if(piohOptHeader->SectionAlignment < piohOptHeader->FileAlignment)
380 DIE(("The section alignment is smaller than the file alignment\n"));
381
382 nSectionAlignment = piohOptHeader->SectionAlignment;
383 nFileAlignment = piohOptHeader->FileAlignment;
384
385 if(!IsPowerOf2(nSectionAlignment) || !IsPowerOf2(nFileAlignment))
386 DIE(("The section alignment (%u) and file alignment (%u) aren't both powers of 2\n", nSectionAlignment, nFileAlignment));
387 }
388 else
389 {
390 nSectionAlignment = PAGE_SIZE;
391 nFileAlignment = PAGE_SIZE;
392 }
393
394 ASSERT(IsPowerOf2(nSectionAlignment));
395 ASSERT(IsPowerOf2(nFileAlignment));
396
397 switch(piohOptHeader->Magic)
398 {
399 /* PE32 */
400 case IMAGE_NT_OPTIONAL_HDR32_MAGIC:
401 {
402 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, ImageBase))
403 ImageBase = piohOptHeader->ImageBase;
404
405 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfImage))
406 ImageSectionObject->ImageInformation.ImageFileSize = piohOptHeader->SizeOfImage;
407
408 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackReserve))
409 ImageSectionObject->ImageInformation.MaximumStackSize = piohOptHeader->SizeOfStackReserve;
410
411 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfStackCommit))
412 ImageSectionObject->ImageInformation.CommittedStackSize = piohOptHeader->SizeOfStackCommit;
413
414 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, Subsystem))
415 {
416 ImageSectionObject->ImageInformation.SubSystemType = piohOptHeader->Subsystem;
417
418 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
419 RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, MajorSubsystemVersion))
420 {
421 ImageSectionObject->ImageInformation.SubSystemMinorVersion = piohOptHeader->MinorSubsystemVersion;
422 ImageSectionObject->ImageInformation.SubSystemMajorVersion = piohOptHeader->MajorSubsystemVersion;
423 }
424 }
425
426 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
427 {
428 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
429 piohOptHeader->AddressOfEntryPoint);
430 }
431
432 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfCode))
433 ImageSectionObject->ImageInformation.ImageContainsCode = piohOptHeader->SizeOfCode != 0;
434 else
435 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
436
437 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, AddressOfEntryPoint))
438 {
439 if (piohOptHeader->AddressOfEntryPoint == 0)
440 {
441 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
442 }
443 }
444
445 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, LoaderFlags))
446 ImageSectionObject->ImageInformation.LoaderFlags = piohOptHeader->LoaderFlags;
447
448 if (RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, DllCharacteristics))
449 {
450 ImageSectionObject->ImageInformation.DllCharacteristics = piohOptHeader->DllCharacteristics;
451
452 /*
453 * Since we don't really implement SxS yet and LD doesn't supoprt /ALLOWISOLATION:NO, hard-code
454 * this flag here, which will prevent the loader and other code from doing any .manifest or SxS
455 * magic to any binary.
456 *
457 * This will break applications that depend on SxS when running with real Windows Kernel32/SxS/etc
458 * but honestly that's not tested. It will also break them when running no ReactOS once we implement
459 * the SxS support -- at which point, duh, this should be removed.
460 *
461 * But right now, any app depending on SxS is already broken anyway, so this flag only helps.
462 */
463 ImageSectionObject->ImageInformation.DllCharacteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
464 }
465
466 break;
467 }
468 #ifdef _WIN64
469 /* PE64 */
470 case IMAGE_NT_OPTIONAL_HDR64_MAGIC:
471 {
472 const IMAGE_OPTIONAL_HEADER64 * pioh64OptHeader;
473
474 pioh64OptHeader = (const IMAGE_OPTIONAL_HEADER64 *)piohOptHeader;
475
476 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, ImageBase))
477 {
478 ImageBase = pioh64OptHeader->ImageBase;
479 if(pioh64OptHeader->ImageBase > MAXULONG_PTR)
480 DIE(("ImageBase exceeds the address space\n"));
481 }
482
483 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfImage))
484 {
485 if(pioh64OptHeader->SizeOfImage > MAXULONG_PTR)
486 DIE(("SizeOfImage exceeds the address space\n"));
487
488 ImageSectionObject->ImageInformation.ImageFileSize = pioh64OptHeader->SizeOfImage;
489 }
490
491 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackReserve))
492 {
493 if(pioh64OptHeader->SizeOfStackReserve > MAXULONG_PTR)
494 DIE(("SizeOfStackReserve exceeds the address space\n"));
495
496 ImageSectionObject->ImageInformation.MaximumStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackReserve;
497 }
498
499 if(RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfStackCommit))
500 {
501 if(pioh64OptHeader->SizeOfStackCommit > MAXULONG_PTR)
502 DIE(("SizeOfStackCommit exceeds the address space\n"));
503
504 ImageSectionObject->ImageInformation.CommittedStackSize = (ULONG_PTR) pioh64OptHeader->SizeOfStackCommit;
505 }
506
507 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, Subsystem))
508 {
509 ImageSectionObject->ImageInformation.SubSystemType = pioh64OptHeader->Subsystem;
510
511 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MinorSubsystemVersion) &&
512 RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, MajorSubsystemVersion))
513 {
514 ImageSectionObject->ImageInformation.SubSystemMinorVersion = pioh64OptHeader->MinorSubsystemVersion;
515 ImageSectionObject->ImageInformation.SubSystemMajorVersion = pioh64OptHeader->MajorSubsystemVersion;
516 }
517 }
518
519 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
520 {
521 ImageSectionObject->ImageInformation.TransferAddress = (PVOID) (ImageBase +
522 pioh64OptHeader->AddressOfEntryPoint);
523 }
524
525 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, SizeOfCode))
526 ImageSectionObject->ImageInformation.ImageContainsCode = pioh64OptHeader->SizeOfCode != 0;
527 else
528 ImageSectionObject->ImageInformation.ImageContainsCode = TRUE;
529
530 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, AddressOfEntryPoint))
531 {
532 if (pioh64OptHeader->AddressOfEntryPoint == 0)
533 {
534 ImageSectionObject->ImageInformation.ImageContainsCode = FALSE;
535 }
536 }
537
538 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, LoaderFlags))
539 ImageSectionObject->ImageInformation.LoaderFlags = pioh64OptHeader->LoaderFlags;
540
541 if (RTL_CONTAINS_FIELD(pioh64OptHeader, cbOptHeaderSize, DllCharacteristics))
542 ImageSectionObject->ImageInformation.DllCharacteristics = pioh64OptHeader->DllCharacteristics;
543
544 break;
545 }
546 #endif // _WIN64
547 }
548
549 /* [1], section 3.4.2 */
550 if((ULONG_PTR)ImageBase % 0x10000)
551 DIE(("ImageBase is not aligned on a 64KB boundary"));
552
553 ImageSectionObject->ImageInformation.ImageCharacteristics = pinhNtHeader->FileHeader.Characteristics;
554 ImageSectionObject->ImageInformation.Machine = pinhNtHeader->FileHeader.Machine;
555 ImageSectionObject->ImageInformation.GpValue = 0;
556 ImageSectionObject->ImageInformation.ZeroBits = 0;
557 ImageSectionObject->BasedAddress = (PVOID)ImageBase;
558
559 /* SECTION HEADERS */
560 nStatus = STATUS_INVALID_IMAGE_FORMAT;
561
562 /* see [1], section 3.3 */
563 if(pinhNtHeader->FileHeader.NumberOfSections > 96)
564 DIE(("Too many sections, NumberOfSections is %u\n", pinhNtHeader->FileHeader.NumberOfSections));
565
566 /*
567 * the additional segment is for the file's headers. They need to be present for
568 * the benefit of the dynamic loader (to locate exports, defaults for thread
569 * parameters, resources, etc.)
570 */
571 ImageSectionObject->NrSegments = pinhNtHeader->FileHeader.NumberOfSections + 1;
572
573 /* file offset for the section headers */
574 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, pidhDosHeader->e_lfanew, FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader)))
575 DIE(("Offset overflow\n"));
576
577 if(!Intsafe_AddULong32(&cbSectionHeadersOffset, cbSectionHeadersOffset, pinhNtHeader->FileHeader.SizeOfOptionalHeader))
578 DIE(("Offset overflow\n"));
579
580 /* size of the section headers */
581 ASSERT(Intsafe_CanMulULong32(pinhNtHeader->FileHeader.NumberOfSections, sizeof(IMAGE_SECTION_HEADER)));
582 cbSectionHeadersSize = pinhNtHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
583
584 if(!Intsafe_AddULong32(&cbSectionHeadersOffsetSize, cbSectionHeadersOffset, cbSectionHeadersSize))
585 DIE(("Section headers too large\n"));
586
587 /* size of the executable's headers */
588 if(RTL_CONTAINS_FIELD(piohOptHeader, cbOptHeaderSize, SizeOfHeaders))
589 {
590 // if(!IsAligned(piohOptHeader->SizeOfHeaders, nFileAlignment))
591 // DIE(("SizeOfHeaders is not aligned\n"));
592
593 if(cbSectionHeadersSize > piohOptHeader->SizeOfHeaders)
594 DIE(("The section headers overflow SizeOfHeaders\n"));
595
596 cbHeadersSize = piohOptHeader->SizeOfHeaders;
597 }
598 else if(!AlignUp(&cbHeadersSize, cbSectionHeadersOffsetSize, nFileAlignment))
599 DIE(("Overflow aligning the size of headers\n"));
600
601 if(pBuffer)
602 {
603 ExFreePool(pBuffer);
604 pBuffer = NULL;
605 }
606 /* WARNING: pinhNtHeader IS NO LONGER USABLE */
607 /* WARNING: piohOptHeader IS NO LONGER USABLE */
608 /* WARNING: pioh64OptHeader IS NO LONGER USABLE */
609
610 if(FileHeaderSize < cbSectionHeadersOffsetSize)
611 pishSectionHeaders = NULL;
612 else
613 {
614 /*
615 * we already know that Intsafe_CanOffsetPointer(FileHeader, FileHeaderSize),
616 * and FileHeaderSize >= cbSectionHeadersOffsetSize, so this holds true too
617 */
618 ASSERT(Intsafe_CanOffsetPointer(FileHeader, cbSectionHeadersOffset));
619 pishSectionHeaders = (PVOID)((UINT_PTR)FileHeader + cbSectionHeadersOffset);
620 }
621
622 /*
623 * the buffer doesn't contain the section headers, or the alignment is wrong:
624 * read the headers from the file
625 */
626 if(FileHeaderSize < cbSectionHeadersOffsetSize ||
627 (UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
628 {
629 PVOID pData;
630 ULONG cbReadSize;
631
632 lnOffset.QuadPart = cbSectionHeadersOffset;
633
634 /* read the header from the file */
635 nStatus = ReadFileCb(File, &lnOffset, cbSectionHeadersSize, &pData, &pBuffer, &cbReadSize);
636
637 if(!NT_SUCCESS(nStatus))
638 DIE(("ReadFile failed with status %08X\n", nStatus));
639
640 ASSERT(pData);
641 ASSERT(pBuffer);
642 ASSERT(cbReadSize > 0);
643
644 nStatus = STATUS_INVALID_IMAGE_FORMAT;
645
646 /* the buffer doesn't contain all the section headers */
647 if(cbReadSize < cbSectionHeadersSize)
648 DIE(("The file doesn't contain all of the section headers\n"));
649
650 pishSectionHeaders = pData;
651
652 /* object still not aligned: copy it to the beginning of the buffer */
653 if((UINT_PTR)pishSectionHeaders % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) != 0)
654 {
655 ASSERT((UINT_PTR)pBuffer % TYPE_ALIGNMENT(IMAGE_SECTION_HEADER) == 0);
656 RtlMoveMemory(pBuffer, pData, cbReadSize);
657 pishSectionHeaders = pBuffer;
658 }
659 }
660
661 /* SEGMENTS */
662 /* allocate the segments */
663 nStatus = STATUS_INSUFFICIENT_RESOURCES;
664 ImageSectionObject->Segments = AllocateSegmentsCb(ImageSectionObject->NrSegments);
665
666 if(ImageSectionObject->Segments == NULL)
667 DIE(("AllocateSegments failed\n"));
668
669 /* initialize the headers segment */
670 pssSegments = ImageSectionObject->Segments;
671
672 // ASSERT(IsAligned(cbHeadersSize, nFileAlignment));
673
674 if(!AlignUp(&nFileSizeOfHeaders, cbHeadersSize, nFileAlignment))
675 DIE(("Cannot align the size of the section headers\n"));
676
677 nPrevVirtualEndOfSegment = ALIGN_UP_BY(cbHeadersSize, nSectionAlignment);
678 if (nPrevVirtualEndOfSegment < cbHeadersSize)
679 DIE(("Cannot align the size of the section headers\n"));
680
681 pssSegments[0].Image.FileOffset = 0;
682 pssSegments[0].Protection = PAGE_READONLY;
683 pssSegments[0].Length.QuadPart = nPrevVirtualEndOfSegment;
684 pssSegments[0].RawLength.QuadPart = nFileSizeOfHeaders;
685 pssSegments[0].Image.VirtualAddress = 0;
686 pssSegments[0].Image.Characteristics = IMAGE_SCN_CNT_INITIALIZED_DATA;
687 pssSegments[0].WriteCopy = TRUE;
688
689 /* skip the headers segment */
690 ++ pssSegments;
691
692 nStatus = STATUS_INVALID_IMAGE_FORMAT;
693
694 /* convert the executable sections into segments. See also [1], section 4 */
695 for(i = 0; i < ImageSectionObject->NrSegments - 1; ++ i)
696 {
697 ULONG nCharacteristics;
698
699 /* validate the alignment */
700 if(!IsAligned(pishSectionHeaders[i].VirtualAddress, nSectionAlignment))
701 DIE(("Image.VirtualAddress[%u] is not aligned\n", i));
702
703 /* sections must be contiguous, ordered by base address and non-overlapping */
704 if(pishSectionHeaders[i].VirtualAddress != nPrevVirtualEndOfSegment)
705 DIE(("Memory gap between section %u and the previous\n", i));
706
707 /* ignore explicit BSS sections */
708 if(pishSectionHeaders[i].SizeOfRawData != 0)
709 {
710 /* validate the alignment */
711 #if 0
712 /* Yes, this should be a multiple of FileAlignment, but there's
713 * stuff out there that isn't. We can cope with that
714 */
715 if(!IsAligned(pishSectionHeaders[i].SizeOfRawData, nFileAlignment))
716 DIE(("SizeOfRawData[%u] is not aligned\n", i));
717 #endif
718
719 // if(!IsAligned(pishSectionHeaders[i].PointerToRawData, nFileAlignment))
720 // DIE(("PointerToRawData[%u] is not aligned\n", i));
721
722 /* conversion */
723 pssSegments[i].Image.FileOffset = pishSectionHeaders[i].PointerToRawData;
724 pssSegments[i].RawLength.QuadPart = pishSectionHeaders[i].SizeOfRawData;
725 }
726 else
727 {
728 ASSERT(pssSegments[i].Image.FileOffset == 0);
729 ASSERT(pssSegments[i].RawLength.QuadPart == 0);
730 }
731
732 ASSERT(Intsafe_CanAddLong64(pssSegments[i].Image.FileOffset, pssSegments[i].RawLength.QuadPart));
733
734 nCharacteristics = pishSectionHeaders[i].Characteristics;
735
736 /* no explicit protection */
737 if((nCharacteristics & (IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) == 0)
738 {
739 if(nCharacteristics & IMAGE_SCN_CNT_CODE)
740 nCharacteristics |= IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ;
741
742 if(nCharacteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
743 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
744
745 if(nCharacteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
746 nCharacteristics |= IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
747 }
748
749 /* see table above */
750 pssSegments[i].Protection = SectionCharacteristicsToProtect[nCharacteristics >> 28];
751 pssSegments[i].WriteCopy = !(nCharacteristics & IMAGE_SCN_MEM_SHARED);
752
753 if(pishSectionHeaders[i].Misc.VirtualSize == 0 || pishSectionHeaders[i].Misc.VirtualSize < pishSectionHeaders[i].SizeOfRawData)
754 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].SizeOfRawData;
755 else
756 pssSegments[i].Length.QuadPart = pishSectionHeaders[i].Misc.VirtualSize;
757
758 pssSegments[i].Length.LowPart = ALIGN_UP_BY(pssSegments[i].Length.LowPart, nSectionAlignment);
759 /* FIXME: always false */
760 if (pssSegments[i].Length.QuadPart < pssSegments[i].Length.QuadPart)
761 DIE(("Cannot align the virtual size of section %u\n", i));
762
763 if(pssSegments[i].Length.QuadPart == 0)
764 DIE(("Virtual size of section %u is null\n", i));
765
766 pssSegments[i].Image.VirtualAddress = pishSectionHeaders[i].VirtualAddress;
767 pssSegments[i].Image.Characteristics = pishSectionHeaders[i].Characteristics;
768
769 /* ensure the memory image is no larger than 4GB */
770 nPrevVirtualEndOfSegment = (ULONG_PTR)(pssSegments[i].Image.VirtualAddress + pssSegments[i].Length.QuadPart);
771 if (nPrevVirtualEndOfSegment < pssSegments[i].Image.VirtualAddress)
772 DIE(("The image is too large\n"));
773 }
774
775 if(nSectionAlignment >= PAGE_SIZE)
776 *Flags |= EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED;
777
778 /* Success */
779 nStatus = STATUS_SUCCESS;// STATUS_ROS_EXEFMT_LOADED_FORMAT | EXEFMT_LOADED_PE32;
780
781 l_Return:
782 if(pBuffer)
783 ExFreePool(pBuffer);
784
785 return nStatus;
786 }
787
788 /*
789 * FUNCTION: Waits in kernel mode indefinitely for a file object lock.
790 * ARGUMENTS: PFILE_OBJECT to wait for.
791 * RETURNS: Status of the wait.
792 */
793 NTSTATUS
794 MmspWaitForFileLock(PFILE_OBJECT File)
795 {
796 return STATUS_SUCCESS;
797 //return KeWaitForSingleObject(&File->Lock, 0, KernelMode, FALSE, NULL);
798 }
799
800 VOID
801 NTAPI
802 MmFreeSectionSegments(PFILE_OBJECT FileObject)
803 {
804 if (FileObject->SectionObjectPointer->ImageSectionObject != NULL)
805 {
806 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
807 PMM_SECTION_SEGMENT SectionSegments;
808 ULONG NrSegments;
809 ULONG i;
810
811 ImageSectionObject = (PMM_IMAGE_SECTION_OBJECT)FileObject->SectionObjectPointer->ImageSectionObject;
812 NrSegments = ImageSectionObject->NrSegments;
813 SectionSegments = ImageSectionObject->Segments;
814 for (i = 0; i < NrSegments; i++)
815 {
816 if (SectionSegments[i].ReferenceCount != 0)
817 {
818 DPRINT1("Image segment %lu still referenced (was %lu)\n", i,
819 SectionSegments[i].ReferenceCount);
820 KeBugCheck(MEMORY_MANAGEMENT);
821 }
822 MmFreePageTablesSectionSegment(&SectionSegments[i], NULL);
823 }
824 ExFreePool(ImageSectionObject->Segments);
825 ExFreePool(ImageSectionObject);
826 FileObject->SectionObjectPointer->ImageSectionObject = NULL;
827 }
828 if (FileObject->SectionObjectPointer->DataSectionObject != NULL)
829 {
830 PMM_SECTION_SEGMENT Segment;
831
832 Segment = (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
833 DataSectionObject;
834
835 if (Segment->ReferenceCount != 0)
836 {
837 DPRINT1("Data segment still referenced\n");
838 KeBugCheck(MEMORY_MANAGEMENT);
839 }
840 MmFreePageTablesSectionSegment(Segment, NULL);
841 ExFreePool(Segment);
842 FileObject->SectionObjectPointer->DataSectionObject = NULL;
843 }
844 }
845
846 VOID
847 NTAPI
848 MmSharePageEntrySectionSegment(PMM_SECTION_SEGMENT Segment,
849 PLARGE_INTEGER Offset)
850 {
851 ULONG_PTR Entry;
852
853 Entry = MmGetPageEntrySectionSegment(Segment, Offset);
854 if (Entry == 0)
855 {
856 DPRINT1("Entry == 0 for MmSharePageEntrySectionSegment\n");
857 KeBugCheck(MEMORY_MANAGEMENT);
858 }
859 if (SHARE_COUNT_FROM_SSE(Entry) == MAX_SHARE_COUNT)
860 {
861 DPRINT1("Maximum share count reached\n");
862 KeBugCheck(MEMORY_MANAGEMENT);
863 }
864 if (IS_SWAP_FROM_SSE(Entry))
865 {
866 KeBugCheck(MEMORY_MANAGEMENT);
867 }
868 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
869 MmSetPageEntrySectionSegment(Segment, Offset, Entry);
870 }
871
872 BOOLEAN
873 NTAPI
874 MmUnsharePageEntrySectionSegment(PROS_SECTION_OBJECT Section,
875 PMM_SECTION_SEGMENT Segment,
876 PLARGE_INTEGER Offset,
877 BOOLEAN Dirty,
878 BOOLEAN PageOut,
879 ULONG_PTR *InEntry)
880 {
881 ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment, Offset);
882 BOOLEAN IsDirectMapped = FALSE;
883
884 if (Entry == 0)
885 {
886 DPRINT1("Entry == 0 for MmUnsharePageEntrySectionSegment\n");
887 KeBugCheck(MEMORY_MANAGEMENT);
888 }
889 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
890 {
891 DPRINT1("Zero share count for unshare (Seg %p Offset %x Page %x)\n", Segment, Offset->LowPart, PFN_FROM_SSE(Entry));
892 KeBugCheck(MEMORY_MANAGEMENT);
893 }
894 if (IS_SWAP_FROM_SSE(Entry))
895 {
896 KeBugCheck(MEMORY_MANAGEMENT);
897 }
898 Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) - 1);
899 /*
900 * If we reducing the share count of this entry to zero then set the entry
901 * to zero and tell the cache the page is no longer mapped.
902 */
903 if (SHARE_COUNT_FROM_SSE(Entry) == 0)
904 {
905 PFILE_OBJECT FileObject;
906 SWAPENTRY SavedSwapEntry;
907 PFN_NUMBER Page;
908 #ifndef NEWCC
909 PROS_SHARED_CACHE_MAP SharedCacheMap;
910 BOOLEAN IsImageSection;
911 LARGE_INTEGER FileOffset;
912
913 FileOffset.QuadPart = Offset->QuadPart + Segment->Image.FileOffset;
914 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
915 #endif
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.QuadPart, 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 LONGLONG 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, 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 LONGLONG 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 LONGLONG BaseOffset;
1077 LONGLONG FileOffset;
1078 PVOID BaseAddress;
1079 BOOLEAN UptoDate;
1080 PROS_VACB Vacb;
1081 PFILE_OBJECT FileObject;
1082 NTSTATUS Status;
1083 LONGLONG RawLength;
1084 PROS_SHARED_CACHE_MAP SharedCacheMap;
1085 BOOLEAN IsImageSection;
1086 LONGLONG Length;
1087
1088 FileObject = MemoryArea->Data.SectionData.Section->FileObject;
1089 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1090 RawLength = 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 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 LONGLONG 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 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 = 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 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 LONGLONG 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 - MA_GetStartingAddress(MemoryArea)
1324 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1325
1326 Segment = MemoryArea->Data.SectionData.Segment;
1327 Section = MemoryArea->Data.SectionData.Section;
1328 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
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, 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 - MA_GetStartingAddress(MemoryArea)
1701 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1702
1703 Segment = MemoryArea->Data.SectionData.Segment;
1704 Section = MemoryArea->Data.SectionData.Section;
1705 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
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, 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 &WasDirty,
1838 &Page);
1839 if (WasDirty)
1840 {
1841 PageOutContext->WasDirty = TRUE;
1842 }
1843 if (!PageOutContext->Private)
1844 {
1845 MmLockSectionSegment(PageOutContext->Segment);
1846 MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section,
1847 PageOutContext->Segment,
1848 &PageOutContext->Offset,
1849 PageOutContext->WasDirty,
1850 TRUE,
1851 &PageOutContext->SectionEntry);
1852 MmUnlockSectionSegment(PageOutContext->Segment);
1853 }
1854 if (Process)
1855 {
1856 MmUnlockAddressSpace(&Process->Vm);
1857 }
1858
1859 if (PageOutContext->Private)
1860 {
1861 MmReleasePageMemoryConsumer(MC_USER, Page);
1862 }
1863 }
1864
1865 NTSTATUS
1866 NTAPI
1867 MmPageOutSectionView(PMMSUPPORT AddressSpace,
1868 MEMORY_AREA* MemoryArea,
1869 PVOID Address, ULONG_PTR Entry)
1870 {
1871 PFN_NUMBER Page;
1872 MM_SECTION_PAGEOUT_CONTEXT Context;
1873 SWAPENTRY SwapEntry;
1874 NTSTATUS Status;
1875 #ifndef NEWCC
1876 ULONGLONG FileOffset;
1877 PFILE_OBJECT FileObject;
1878 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
1879 BOOLEAN IsImageSection;
1880 #endif
1881 BOOLEAN DirectMapped;
1882 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1883 KIRQL OldIrql;
1884
1885 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1886
1887 /*
1888 * Get the segment and section.
1889 */
1890 Context.Segment = MemoryArea->Data.SectionData.Segment;
1891 Context.Section = MemoryArea->Data.SectionData.Section;
1892 Context.SectionEntry = Entry;
1893 Context.CallingProcess = Process;
1894
1895 Context.Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1896 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1897
1898 DirectMapped = FALSE;
1899
1900 MmLockSectionSegment(Context.Segment);
1901
1902 #ifndef NEWCC
1903 FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset;
1904 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1905 FileObject = Context.Section->FileObject;
1906
1907 if (FileObject != NULL &&
1908 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1909 {
1910 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1911
1912 /*
1913 * If the file system is letting us go directly to the cache and the
1914 * memory area was mapped at an offset in the file which is page aligned
1915 * then note this is a direct mapped page.
1916 */
1917 if ((FileOffset % PAGE_SIZE) == 0 &&
1918 (Context.Offset.QuadPart + PAGE_SIZE <= Context.Segment->RawLength.QuadPart || !IsImageSection))
1919 {
1920 DirectMapped = TRUE;
1921 }
1922 }
1923 #endif
1924
1925
1926 /*
1927 * This should never happen since mappings of physical memory are never
1928 * placed in the rmap lists.
1929 */
1930 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1931 {
1932 DPRINT1("Trying to page out from physical memory section address 0x%p "
1933 "process %p\n", Address,
1934 Process ? Process->UniqueProcessId : 0);
1935 KeBugCheck(MEMORY_MANAGEMENT);
1936 }
1937
1938 /*
1939 * Get the section segment entry and the physical address.
1940 */
1941 if (!MmIsPagePresent(Process, Address))
1942 {
1943 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
1944 Process ? Process->UniqueProcessId : 0, Address);
1945 KeBugCheck(MEMORY_MANAGEMENT);
1946 }
1947 Page = MmGetPfnForProcess(Process, Address);
1948 SwapEntry = MmGetSavedSwapEntryPage(Page);
1949
1950 /*
1951 * Check the reference count to ensure this page can be paged out
1952 */
1953 if (MmGetReferenceCountPage(Page) != 1)
1954 {
1955 DPRINT("Cannot page out locked section page: 0x%lu (RefCount: %lu)\n",
1956 Page, MmGetReferenceCountPage(Page));
1957 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
1958 MmUnlockSectionSegment(Context.Segment);
1959 return STATUS_UNSUCCESSFUL;
1960 }
1961
1962 /*
1963 * Prepare the context structure for the rmap delete call.
1964 */
1965 MmUnlockSectionSegment(Context.Segment);
1966 Context.WasDirty = FALSE;
1967 if (Context.Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1968 IS_SWAP_FROM_SSE(Entry) ||
1969 PFN_FROM_SSE(Entry) != Page)
1970 {
1971 Context.Private = TRUE;
1972 }
1973 else
1974 {
1975 Context.Private = FALSE;
1976 }
1977
1978 /*
1979 * Take an additional reference to the page or the VACB.
1980 */
1981 if (DirectMapped && !Context.Private)
1982 {
1983 if(!MiIsPageFromCache(MemoryArea, Context.Offset.QuadPart))
1984 {
1985 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
1986 KeBugCheck(MEMORY_MANAGEMENT);
1987 }
1988 }
1989 else
1990 {
1991 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
1992 MmReferencePage(Page);
1993 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
1994 }
1995
1996 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
1997
1998 /* Since we passed in a surrogate, we'll get back the page entry
1999 * state in our context. This is intended to make intermediate
2000 * decrements of share count not release the wait entry.
2001 */
2002 Entry = Context.SectionEntry;
2003
2004 /*
2005 * If this wasn't a private page then we should have reduced the entry to
2006 * zero by deleting all the rmaps.
2007 */
2008 if (!Context.Private && Entry != 0)
2009 {
2010 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
2011 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2012 {
2013 KeBugCheckEx(MEMORY_MANAGEMENT, Entry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2014 }
2015 }
2016
2017 /*
2018 * If the page wasn't dirty then we can just free it as for a readonly page.
2019 * Since we unmapped all the mappings above we know it will not suddenly
2020 * become dirty.
2021 * If the page is from a pagefile section and has no swap entry,
2022 * we can't free the page at this point.
2023 */
2024 SwapEntry = MmGetSavedSwapEntryPage(Page);
2025 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
2026 {
2027 if (Context.Private)
2028 {
2029 DPRINT1("Found a %s private page (address %p) in a pagefile segment.\n",
2030 Context.WasDirty ? "dirty" : "clean", Address);
2031 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2032 }
2033 if (!Context.WasDirty && SwapEntry != 0)
2034 {
2035 MmSetSavedSwapEntryPage(Page, 0);
2036 MmLockSectionSegment(Context.Segment);
2037 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2038 MmUnlockSectionSegment(Context.Segment);
2039 MmReleasePageMemoryConsumer(MC_USER, Page);
2040 MiSetPageEvent(NULL, NULL);
2041 return(STATUS_SUCCESS);
2042 }
2043 }
2044 else if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2045 {
2046 if (Context.Private)
2047 {
2048 DPRINT1("Found a %s private page (address %p) in a shared section segment.\n",
2049 Context.WasDirty ? "dirty" : "clean", Address);
2050 KeBugCheckEx(MEMORY_MANAGEMENT, Page, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2051 }
2052 if (!Context.WasDirty || SwapEntry != 0)
2053 {
2054 MmSetSavedSwapEntryPage(Page, 0);
2055 if (SwapEntry != 0)
2056 {
2057 MmLockSectionSegment(Context.Segment);
2058 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2059 MmUnlockSectionSegment(Context.Segment);
2060 }
2061 MmReleasePageMemoryConsumer(MC_USER, Page);
2062 MiSetPageEvent(NULL, NULL);
2063 return(STATUS_SUCCESS);
2064 }
2065 }
2066 else if (!Context.Private && DirectMapped)
2067 {
2068 if (SwapEntry != 0)
2069 {
2070 DPRINT1("Found a swapentry for a non private and direct mapped page (address %p)\n",
2071 Address);
2072 KeBugCheckEx(MEMORY_MANAGEMENT, STATUS_UNSUCCESSFUL, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address);
2073 }
2074 #ifndef NEWCC
2075 Status = CcRosUnmapVacb(SharedCacheMap, FileOffset, FALSE);
2076 #else
2077 Status = STATUS_SUCCESS;
2078 #endif
2079 #ifndef NEWCC
2080 if (!NT_SUCCESS(Status))
2081 {
2082 DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
2083 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)SharedCacheMap, (ULONG_PTR)FileOffset, (ULONG_PTR)Address);
2084 }
2085 #endif
2086 MiSetPageEvent(NULL, NULL);
2087 return(STATUS_SUCCESS);
2088 }
2089 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
2090 {
2091 if (SwapEntry != 0)
2092 {
2093 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %p)\n",
2094 Address);
2095 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, Page, (ULONG_PTR)Process, (ULONG_PTR)Address);
2096 }
2097 MmReleasePageMemoryConsumer(MC_USER, Page);
2098 MiSetPageEvent(NULL, NULL);
2099 return(STATUS_SUCCESS);
2100 }
2101 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
2102 {
2103 DPRINT("Not dirty and private and not swapped (%p:%p)\n", Process, Address);
2104 MmSetSavedSwapEntryPage(Page, 0);
2105 MmLockAddressSpace(AddressSpace);
2106 Status = MmCreatePageFileMapping(Process,
2107 Address,
2108 SwapEntry);
2109 MmUnlockAddressSpace(AddressSpace);
2110 if (!NT_SUCCESS(Status))
2111 {
2112 DPRINT1("Status %x Swapping out %p:%p\n", Status, Process, Address);
2113 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2114 }
2115 MmReleasePageMemoryConsumer(MC_USER, Page);
2116 MiSetPageEvent(NULL, NULL);
2117 return(STATUS_SUCCESS);
2118 }
2119
2120 /*
2121 * If necessary, allocate an entry in the paging file for this page
2122 */
2123 if (SwapEntry == 0)
2124 {
2125 SwapEntry = MmAllocSwapPage();
2126 if (SwapEntry == 0)
2127 {
2128 MmShowOutOfSpaceMessagePagingFile();
2129 MmLockAddressSpace(AddressSpace);
2130 /*
2131 * For private pages restore the old mappings.
2132 */
2133 if (Context.Private)
2134 {
2135 Status = MmCreateVirtualMapping(Process,
2136 Address,
2137 MemoryArea->Protect,
2138 &Page,
2139 1);
2140 MmSetDirtyPage(Process, Address);
2141 MmInsertRmap(Page,
2142 Process,
2143 Address);
2144 }
2145 else
2146 {
2147 ULONG_PTR OldEntry;
2148 /*
2149 * For non-private pages if the page wasn't direct mapped then
2150 * set it back into the section segment entry so we don't loose
2151 * our copy. Otherwise it will be handled by the cache manager.
2152 */
2153 Status = MmCreateVirtualMapping(Process,
2154 Address,
2155 MemoryArea->Protect,
2156 &Page,
2157 1);
2158 MmSetDirtyPage(Process, Address);
2159 MmInsertRmap(Page,
2160 Process,
2161 Address);
2162 // If we got here, the previous entry should have been a wait
2163 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2164 MmLockSectionSegment(Context.Segment);
2165 OldEntry = MmGetPageEntrySectionSegment(Context.Segment, &Context.Offset);
2166 ASSERT(OldEntry == 0 || OldEntry == MAKE_SWAP_SSE(MM_WAIT_ENTRY));
2167 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2168 MmUnlockSectionSegment(Context.Segment);
2169 }
2170 MmUnlockAddressSpace(AddressSpace);
2171 MiSetPageEvent(NULL, NULL);
2172 return(STATUS_PAGEFILE_QUOTA);
2173 }
2174 }
2175
2176 /*
2177 * Write the page to the pagefile
2178 */
2179 Status = MmWriteToSwapPage(SwapEntry, Page);
2180 if (!NT_SUCCESS(Status))
2181 {
2182 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2183 Status);
2184 /*
2185 * As above: undo our actions.
2186 * FIXME: Also free the swap page.
2187 */
2188 MmLockAddressSpace(AddressSpace);
2189 if (Context.Private)
2190 {
2191 Status = MmCreateVirtualMapping(Process,
2192 Address,
2193 MemoryArea->Protect,
2194 &Page,
2195 1);
2196 MmSetDirtyPage(Process, Address);
2197 MmInsertRmap(Page,
2198 Process,
2199 Address);
2200 }
2201 else
2202 {
2203 Status = MmCreateVirtualMapping(Process,
2204 Address,
2205 MemoryArea->Protect,
2206 &Page,
2207 1);
2208 MmSetDirtyPage(Process, Address);
2209 MmInsertRmap(Page,
2210 Process,
2211 Address);
2212 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2213 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2214 }
2215 MmUnlockAddressSpace(AddressSpace);
2216 MiSetPageEvent(NULL, NULL);
2217 return(STATUS_UNSUCCESSFUL);
2218 }
2219
2220 /*
2221 * Otherwise we have succeeded.
2222 */
2223 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2224 MmSetSavedSwapEntryPage(Page, 0);
2225 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
2226 Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2227 {
2228 MmLockSectionSegment(Context.Segment);
2229 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2230 MmUnlockSectionSegment(Context.Segment);
2231 }
2232 else
2233 {
2234 MmReleasePageMemoryConsumer(MC_USER, Page);
2235 }
2236
2237 if (Context.Private)
2238 {
2239 MmLockAddressSpace(AddressSpace);
2240 MmLockSectionSegment(Context.Segment);
2241 Status = MmCreatePageFileMapping(Process,
2242 Address,
2243 SwapEntry);
2244 /* We had placed a wait entry upon entry ... replace it before leaving */
2245 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2246 MmUnlockSectionSegment(Context.Segment);
2247 MmUnlockAddressSpace(AddressSpace);
2248 if (!NT_SUCCESS(Status))
2249 {
2250 DPRINT1("Status %x Creating page file mapping for %p:%p\n", Status, Process, Address);
2251 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2252 }
2253 }
2254 else
2255 {
2256 MmLockAddressSpace(AddressSpace);
2257 MmLockSectionSegment(Context.Segment);
2258 Entry = MAKE_SWAP_SSE(SwapEntry);
2259 /* We had placed a wait entry upon entry ... replace it before leaving */
2260 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2261 MmUnlockSectionSegment(Context.Segment);
2262 MmUnlockAddressSpace(AddressSpace);
2263 }
2264
2265 MiSetPageEvent(NULL, NULL);
2266 return(STATUS_SUCCESS);
2267 }
2268
2269 NTSTATUS
2270 NTAPI
2271 MmWritePageSectionView(PMMSUPPORT AddressSpace,
2272 PMEMORY_AREA MemoryArea,
2273 PVOID Address,
2274 ULONG PageEntry)
2275 {
2276 LARGE_INTEGER Offset;
2277 PROS_SECTION_OBJECT Section;
2278 PMM_SECTION_SEGMENT Segment;
2279 PFN_NUMBER Page;
2280 SWAPENTRY SwapEntry;
2281 ULONG_PTR Entry;
2282 BOOLEAN Private;
2283 NTSTATUS Status;
2284 PFILE_OBJECT FileObject;
2285 #ifndef NEWCC
2286 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
2287 #endif
2288 BOOLEAN DirectMapped;
2289 BOOLEAN IsImageSection;
2290 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2291
2292 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2293
2294 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
2295 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2296
2297 /*
2298 * Get the segment and section.
2299 */
2300 Segment = MemoryArea->Data.SectionData.Segment;
2301 Section = MemoryArea->Data.SectionData.Section;
2302 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2303
2304 FileObject = Section->FileObject;
2305 DirectMapped = FALSE;
2306 if (FileObject != NULL &&
2307 !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2308 {
2309 #ifndef NEWCC
2310 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
2311 #endif
2312
2313 /*
2314 * If the file system is letting us go directly to the cache and the
2315 * memory area was mapped at an offset in the file which is page aligned
2316 * then note this is a direct mapped page.
2317 */
2318 if (((Offset.QuadPart + Segment->Image.FileOffset) % PAGE_SIZE) == 0 &&
2319 (Offset.QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
2320 {
2321 DirectMapped = TRUE;
2322 }
2323 }
2324
2325 /*
2326 * This should never happen since mappings of physical memory are never
2327 * placed in the rmap lists.
2328 */
2329 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2330 {
2331 DPRINT1("Trying to write back page from physical memory mapped at %p "
2332 "process %p\n", Address,
2333 Process ? Process->UniqueProcessId : 0);
2334 KeBugCheck(MEMORY_MANAGEMENT);
2335 }
2336
2337 /*
2338 * Get the section segment entry and the physical address.
2339 */
2340 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2341 if (!MmIsPagePresent(Process, Address))
2342 {
2343 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
2344 Process ? Process->UniqueProcessId : 0, Address);
2345 KeBugCheck(MEMORY_MANAGEMENT);
2346 }
2347 Page = MmGetPfnForProcess(Process, Address);
2348 SwapEntry = MmGetSavedSwapEntryPage(Page);
2349
2350 /*
2351 * Check for a private (COWed) page.
2352 */
2353 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2354 IS_SWAP_FROM_SSE(Entry) ||
2355 PFN_FROM_SSE(Entry) != Page)
2356 {
2357 Private = TRUE;
2358 }
2359 else
2360 {
2361 Private = FALSE;
2362 }
2363
2364 /*
2365 * Speculatively set all mappings of the page to clean.
2366 */
2367 MmSetCleanAllRmaps(Page);
2368
2369 /*
2370 * If this page was direct mapped from the cache then the cache manager
2371 * will take care of writing it back to disk.
2372 */
2373 if (DirectMapped && !Private)
2374 {
2375 //LARGE_INTEGER SOffset;
2376 ASSERT(SwapEntry == 0);
2377 //SOffset.QuadPart = Offset.QuadPart + Segment->Image.FileOffset;
2378 #ifndef NEWCC
2379 CcRosMarkDirtyVacb(SharedCacheMap, Offset.QuadPart);
2380 #endif
2381 MmLockSectionSegment(Segment);
2382 MmSetPageEntrySectionSegment(Segment, &Offset, PageEntry);
2383 MmUnlockSectionSegment(Segment);
2384 MiSetPageEvent(NULL, NULL);
2385 return(STATUS_SUCCESS);
2386 }
2387
2388 /*
2389 * If necessary, allocate an entry in the paging file for this page
2390 */
2391 if (SwapEntry == 0)
2392 {
2393 SwapEntry = MmAllocSwapPage();
2394 if (SwapEntry == 0)
2395 {
2396 MmSetDirtyAllRmaps(Page);
2397 MiSetPageEvent(NULL, NULL);
2398 return(STATUS_PAGEFILE_QUOTA);
2399 }
2400 MmSetSavedSwapEntryPage(Page, SwapEntry);
2401 }
2402
2403 /*
2404 * Write the page to the pagefile
2405 */
2406 Status = MmWriteToSwapPage(SwapEntry, Page);
2407 if (!NT_SUCCESS(Status))
2408 {
2409 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2410 Status);
2411 MmSetDirtyAllRmaps(Page);
2412 MiSetPageEvent(NULL, NULL);
2413 return(STATUS_UNSUCCESSFUL);
2414 }
2415
2416 /*
2417 * Otherwise we have succeeded.
2418 */
2419 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2420 MiSetPageEvent(NULL, NULL);
2421 return(STATUS_SUCCESS);
2422 }
2423
2424 static VOID
2425 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
2426 PVOID BaseAddress,
2427 SIZE_T RegionSize,
2428 ULONG OldType,
2429 ULONG OldProtect,
2430 ULONG NewType,
2431 ULONG NewProtect)
2432 {
2433 PMEMORY_AREA MemoryArea;
2434 PMM_SECTION_SEGMENT Segment;
2435 BOOLEAN DoCOW = FALSE;
2436 ULONG i;
2437 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2438
2439 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
2440 ASSERT(MemoryArea != NULL);
2441 Segment = MemoryArea->Data.SectionData.Segment;
2442 MmLockSectionSegment(Segment);
2443
2444 if ((Segment->WriteCopy) &&
2445 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
2446 {
2447 DoCOW = TRUE;
2448 }
2449
2450 if (OldProtect != NewProtect)
2451 {
2452 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
2453 {
2454 SWAPENTRY SwapEntry;
2455 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
2456 ULONG Protect = NewProtect;
2457
2458 /* Wait for a wait entry to disappear */
2459 do
2460 {
2461 MmGetPageFileMapping(Process, Address, &SwapEntry);
2462 if (SwapEntry != MM_WAIT_ENTRY)
2463 break;
2464 MiWaitForPageEvent(Process, Address);
2465 }
2466 while (TRUE);
2467
2468 /*
2469 * If we doing COW for this segment then check if the page is
2470 * already private.
2471 */
2472 if (DoCOW && MmIsPagePresent(Process, Address))
2473 {
2474 LARGE_INTEGER Offset;
2475 ULONG_PTR Entry;
2476 PFN_NUMBER Page;
2477
2478 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
2479 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2480 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2481 /*
2482 * An MM_WAIT_ENTRY is ok in this case... It'll just count as
2483 * IS_SWAP_FROM_SSE and we'll do the right thing.
2484 */
2485 Page = MmGetPfnForProcess(Process, Address);
2486
2487 Protect = PAGE_READONLY;
2488 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2489 IS_SWAP_FROM_SSE(Entry) ||
2490 PFN_FROM_SSE(Entry) != Page)
2491 {
2492 Protect = NewProtect;
2493 }
2494 }
2495
2496 if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
2497 {
2498 MmSetPageProtect(Process, Address,
2499 Protect);
2500 }
2501 }
2502 }
2503
2504 MmUnlockSectionSegment(Segment);
2505 }
2506
2507 NTSTATUS
2508 NTAPI
2509 MmProtectSectionView(PMMSUPPORT AddressSpace,
2510 PMEMORY_AREA MemoryArea,
2511 PVOID BaseAddress,
2512 SIZE_T Length,
2513 ULONG Protect,
2514 PULONG OldProtect)
2515 {
2516 PMM_REGION Region;
2517 NTSTATUS Status;
2518 ULONG_PTR MaxLength;
2519
2520 MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2521 if (Length > MaxLength)
2522 Length = (ULONG)MaxLength;
2523
2524 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2525 &MemoryArea->Data.SectionData.RegionListHead,
2526 BaseAddress, NULL);
2527 ASSERT(Region != NULL);
2528
2529 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2530 Region->Protect != Protect)
2531 {
2532 return STATUS_INVALID_PAGE_PROTECTION;
2533 }
2534
2535 *OldProtect = Region->Protect;
2536 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2537 &MemoryArea->Data.SectionData.RegionListHead,
2538 BaseAddress, Length, Region->Type, Protect,
2539 MmAlterViewAttributes);
2540
2541 return(Status);
2542 }
2543
2544 NTSTATUS NTAPI
2545 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2546 PVOID Address,
2547 PMEMORY_BASIC_INFORMATION Info,
2548 PSIZE_T ResultLength)
2549 {
2550 PMM_REGION Region;
2551 PVOID RegionBaseAddress;
2552 PROS_SECTION_OBJECT Section;
2553 PMM_SECTION_SEGMENT Segment;
2554
2555 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2556 &MemoryArea->Data.SectionData.RegionListHead,
2557 Address, &RegionBaseAddress);
2558 if (Region == NULL)
2559 {
2560 return STATUS_UNSUCCESSFUL;
2561 }
2562
2563 Section = MemoryArea->Data.SectionData.Section;
2564 if (Section->AllocationAttributes & SEC_IMAGE)
2565 {
2566 Segment = MemoryArea->Data.SectionData.Segment;
2567 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2568 Info->Type = MEM_IMAGE;
2569 }
2570 else
2571 {
2572 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2573 Info->Type = MEM_MAPPED;
2574 }
2575 Info->BaseAddress = RegionBaseAddress;
2576 Info->AllocationProtect = MemoryArea->Protect;
2577 Info->RegionSize = Region->Length;
2578 Info->State = MEM_COMMIT;
2579 Info->Protect = Region->Protect;
2580
2581 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2582 return(STATUS_SUCCESS);
2583 }
2584
2585 VOID
2586 NTAPI
2587 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2588 {
2589 ULONG Length;
2590 LARGE_INTEGER Offset;
2591 ULONG_PTR Entry;
2592 SWAPENTRY SavedSwapEntry;
2593 PFN_NUMBER Page;
2594
2595 Page = 0;
2596
2597 MmLockSectionSegment(Segment);
2598
2599 Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
2600 for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
2601 {
2602 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2603 if (Entry)
2604 {
2605 MmSetPageEntrySectionSegment(Segment, &Offset, 0);
2606 if (IS_SWAP_FROM_SSE(Entry))
2607 {
2608 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2609 }
2610 else
2611 {
2612 Page = PFN_FROM_SSE(Entry);
2613 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2614 if (SavedSwapEntry != 0)
2615 {
2616 MmSetSavedSwapEntryPage(Page, 0);
2617 MmFreeSwapPage(SavedSwapEntry);
2618 }
2619 MmReleasePageMemoryConsumer(MC_USER, Page);
2620 }
2621 }
2622 }
2623
2624 MmUnlockSectionSegment(Segment);
2625 }
2626
2627 VOID NTAPI
2628 MmpDeleteSection(PVOID ObjectBody)
2629 {
2630 PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
2631
2632 /* Check if it's an ARM3, or ReactOS section */
2633 if (!MiIsRosSectionObject(Section))
2634 {
2635 MiDeleteARM3Section(ObjectBody);
2636 return;
2637 }
2638
2639 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2640 if (Section->AllocationAttributes & SEC_IMAGE)
2641 {
2642 ULONG i;
2643 ULONG NrSegments;
2644 ULONG RefCount;
2645 PMM_SECTION_SEGMENT SectionSegments;
2646
2647 /*
2648 * NOTE: Section->ImageSection can be NULL for short time
2649 * during the section creating. If we fail for some reason
2650 * until the image section is properly initialized we shouldn't
2651 * process further here.
2652 */
2653 if (Section->ImageSection == NULL)
2654 return;
2655
2656 SectionSegments = Section->ImageSection->Segments;
2657 NrSegments = Section->ImageSection->NrSegments;
2658
2659 for (i = 0; i < NrSegments; i++)
2660 {
2661 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2662 {
2663 MmLockSectionSegment(&SectionSegments[i]);
2664 }
2665 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2666 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2667 {
2668 MmUnlockSectionSegment(&SectionSegments[i]);
2669 if (RefCount == 0)
2670 {
2671 MmpFreePageFileSegment(&SectionSegments[i]);
2672 }
2673 }
2674 }
2675 }
2676 #ifdef NEWCC
2677 else if (Section->Segment && Section->Segment->Flags & MM_DATAFILE_SEGMENT)
2678 {
2679 ULONG RefCount = 0;
2680 PMM_SECTION_SEGMENT Segment = Section->Segment;
2681
2682 if (Segment &&
2683 (RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
2684 {
2685 DPRINT("Freeing section segment\n");
2686 Section->Segment = NULL;
2687 MmFinalizeSegment(Segment);
2688 }
2689 else
2690 {
2691 DPRINT("RefCount %d\n", RefCount);
2692 }
2693 }
2694 #endif
2695 else
2696 {
2697 /*
2698 * NOTE: Section->Segment can be NULL for short time
2699 * during the section creating.
2700 */
2701 if (Section->Segment == NULL)
2702 return;
2703
2704 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2705 {
2706 MmpFreePageFileSegment(Section->Segment);
2707 MmFreePageTablesSectionSegment(Section->Segment, NULL);
2708 ExFreePool(Section->Segment);
2709 Section->Segment = NULL;
2710 }
2711 else
2712 {
2713 (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2714 }
2715 }
2716 if (Section->FileObject != NULL)
2717 {
2718 #ifndef NEWCC
2719 CcRosDereferenceCache(Section->FileObject);
2720 #endif
2721 ObDereferenceObject(Section->FileObject);
2722 Section->FileObject = NULL;
2723 }
2724 }
2725
2726 VOID NTAPI
2727 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2728 IN PVOID Object,
2729 IN ACCESS_MASK GrantedAccess,
2730 IN ULONG ProcessHandleCount,
2731 IN ULONG SystemHandleCount)
2732 {
2733 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2734 }
2735
2736 NTSTATUS
2737 INIT_FUNCTION
2738 NTAPI
2739 MmCreatePhysicalMemorySection(VOID)
2740 {
2741 PROS_SECTION_OBJECT PhysSection;
2742 NTSTATUS Status;
2743 OBJECT_ATTRIBUTES Obj;
2744 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2745 LARGE_INTEGER SectionSize;
2746 HANDLE Handle;
2747
2748 /*
2749 * Create the section mapping physical memory
2750 */
2751 SectionSize.QuadPart = 0xFFFFFFFF;
2752 InitializeObjectAttributes(&Obj,
2753 &Name,
2754 OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
2755 NULL,
2756 NULL);
2757 Status = MmCreateSection((PVOID)&PhysSection,
2758 SECTION_ALL_ACCESS,
2759 &Obj,
2760 &SectionSize,
2761 PAGE_EXECUTE_READWRITE,
2762 SEC_PHYSICALMEMORY,
2763 NULL,
2764 NULL);
2765 if (!NT_SUCCESS(Status))
2766 {
2767 DPRINT1("Failed to create PhysicalMemory section\n");
2768 KeBugCheck(MEMORY_MANAGEMENT);
2769 }
2770 Status = ObInsertObject(PhysSection,
2771 NULL,
2772 SECTION_ALL_ACCESS,
2773 0,
2774 NULL,
2775 &Handle);
2776 if (!NT_SUCCESS(Status))
2777 {
2778 ObDereferenceObject(PhysSection);
2779 }
2780 ObCloseHandle(Handle, KernelMode);
2781 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2782 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2783
2784 return(STATUS_SUCCESS);
2785 }
2786
2787 NTSTATUS
2788 INIT_FUNCTION
2789 NTAPI
2790 MmInitSectionImplementation(VOID)
2791 {
2792 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2793 UNICODE_STRING Name;
2794
2795 DPRINT("Creating Section Object Type\n");
2796
2797 /* Initialize the section based root */
2798 ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2799 MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2800
2801 /* Initialize the Section object type */
2802 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2803 RtlInitUnicodeString(&Name, L"Section");
2804 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2805 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT);
2806 ObjectTypeInitializer.PoolType = PagedPool;
2807 ObjectTypeInitializer.UseDefaultObject = TRUE;
2808 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2809 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2810 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2811 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2812 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2813 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2814
2815 MmCreatePhysicalMemorySection();
2816
2817 return(STATUS_SUCCESS);
2818 }
2819
2820 NTSTATUS
2821 NTAPI
2822 MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
2823 ACCESS_MASK DesiredAccess,
2824 POBJECT_ATTRIBUTES ObjectAttributes,
2825 PLARGE_INTEGER UMaximumSize,
2826 ULONG SectionPageProtection,
2827 ULONG AllocationAttributes)
2828 /*
2829 * Create a section which is backed by the pagefile
2830 */
2831 {
2832 LARGE_INTEGER MaximumSize;
2833 PROS_SECTION_OBJECT Section;
2834 PMM_SECTION_SEGMENT Segment;
2835 NTSTATUS Status;
2836
2837 if (UMaximumSize == NULL)
2838 {
2839 DPRINT1("MmCreatePageFileSection: (UMaximumSize == NULL)\n");
2840 return(STATUS_INVALID_PARAMETER);
2841 }
2842 MaximumSize = *UMaximumSize;
2843
2844 /*
2845 * Create the section
2846 */
2847 Status = ObCreateObject(ExGetPreviousMode(),
2848 MmSectionObjectType,
2849 ObjectAttributes,
2850 ExGetPreviousMode(),
2851 NULL,
2852 sizeof(ROS_SECTION_OBJECT),
2853 0,
2854 0,
2855 (PVOID*)(PVOID)&Section);
2856 if (!NT_SUCCESS(Status))
2857 {
2858 DPRINT1("MmCreatePageFileSection: failed to create object (0x%lx)\n", Status);
2859 return(Status);
2860 }
2861
2862 /*
2863 * Initialize it
2864 */
2865 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2866 Section->Type = 'SC';
2867 Section->Size = 'TN';
2868 Section->SectionPageProtection = SectionPageProtection;
2869 Section->AllocationAttributes = AllocationAttributes;
2870 Section->MaximumSize = MaximumSize;
2871 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2872 TAG_MM_SECTION_SEGMENT);
2873 if (Segment == NULL)
2874 {
2875 ObDereferenceObject(Section);
2876 return(STATUS_NO_MEMORY);
2877 }
2878 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2879 Section->Segment = Segment;
2880 Segment->ReferenceCount = 1;
2881 ExInitializeFastMutex(&Segment->Lock);
2882 Segment->Image.FileOffset = 0;
2883 Segment->Protection = SectionPageProtection;
2884 Segment->RawLength.QuadPart = MaximumSize.u.LowPart;
2885 Segment->Length.QuadPart = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2886 Segment->Flags = MM_PAGEFILE_SEGMENT;
2887 Segment->WriteCopy = FALSE;
2888 Segment->Image.VirtualAddress = 0;
2889 Segment->Image.Characteristics = 0;
2890 *SectionObject = Section;
2891 MiInitializeSectionPageTable(Segment);
2892 return(STATUS_SUCCESS);
2893 }
2894
2895 NTSTATUS
2896 NTAPI
2897 MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject,
2898 ACCESS_MASK DesiredAccess,
2899 POBJECT_ATTRIBUTES ObjectAttributes,
2900 PLARGE_INTEGER UMaximumSize,
2901 ULONG SectionPageProtection,
2902 ULONG AllocationAttributes,
2903 HANDLE FileHandle)
2904 /*
2905 * Create a section backed by a data file
2906 */
2907 {
2908 PROS_SECTION_OBJECT Section;
2909 NTSTATUS Status;
2910 LARGE_INTEGER MaximumSize;
2911 PFILE_OBJECT FileObject;
2912 PMM_SECTION_SEGMENT Segment;
2913 ULONG FileAccess;
2914 IO_STATUS_BLOCK Iosb;
2915 LARGE_INTEGER Offset;
2916 CHAR Buffer;
2917 FILE_STANDARD_INFORMATION FileInfo;
2918 ULONG Length;
2919
2920 /*
2921 * Create the section
2922 */
2923 Status = ObCreateObject(ExGetPreviousMode(),
2924 MmSectionObjectType,
2925 ObjectAttributes,
2926 ExGetPreviousMode(),
2927 NULL,
2928 sizeof(ROS_SECTION_OBJECT),
2929 0,
2930 0,
2931 (PVOID*)&Section);
2932 if (!NT_SUCCESS(Status))
2933 {
2934 return(Status);
2935 }
2936 /*
2937 * Initialize it
2938 */
2939 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2940 Section->Type = 'SC';
2941 Section->Size = 'TN';
2942 Section->SectionPageProtection = SectionPageProtection;
2943 Section->AllocationAttributes = AllocationAttributes;
2944
2945 /*
2946 * Reference the file handle
2947 */
2948 FileAccess = MiArm3GetCorrectFileAccessMask(SectionPageProtection);
2949 Status = ObReferenceObjectByHandle(FileHandle,
2950 FileAccess,
2951 IoFileObjectType,
2952 ExGetPreviousMode(),
2953 (PVOID*)(PVOID)&FileObject,
2954 NULL);
2955 if (!NT_SUCCESS(Status))
2956 {
2957 ObDereferenceObject(Section);
2958 return(Status);
2959 }
2960
2961 /*
2962 * FIXME: This is propably not entirely correct. We can't look into
2963 * the standard FCB header because it might not be initialized yet
2964 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2965 * standard file information is filled on first request).
2966 */
2967 Status = IoQueryFileInformation(FileObject,
2968 FileStandardInformation,
2969 sizeof(FILE_STANDARD_INFORMATION),
2970 &FileInfo,
2971 &Length);
2972 Iosb.Information = Length;
2973 if (!NT_SUCCESS(Status))
2974 {
2975 ObDereferenceObject(Section);
2976 ObDereferenceObject(FileObject);
2977 return Status;
2978 }
2979
2980 /*
2981 * FIXME: Revise this once a locking order for file size changes is
2982 * decided
2983 */
2984 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2985 {
2986 MaximumSize = *UMaximumSize;
2987 }
2988 else
2989 {
2990 MaximumSize = FileInfo.EndOfFile;
2991 /* Mapping zero-sized files isn't allowed. */
2992 if (MaximumSize.QuadPart == 0)
2993 {
2994 ObDereferenceObject(Section);
2995 ObDereferenceObject(FileObject);
2996 return STATUS_MAPPED_FILE_SIZE_ZERO;
2997 }
2998 }
2999
3000 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
3001 {
3002 Status = IoSetInformation(FileObject,
3003 FileAllocationInformation,
3004 sizeof(LARGE_INTEGER),
3005 &MaximumSize);
3006 if (!NT_SUCCESS(Status))
3007 {
3008 ObDereferenceObject(Section);
3009 ObDereferenceObject(FileObject);
3010 return(STATUS_SECTION_NOT_EXTENDED);
3011 }
3012 }
3013
3014 if (FileObject->SectionObjectPointer == NULL ||
3015 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3016 {
3017 /*
3018 * Read a bit so caching is initiated for the file object.
3019 * This is only needed because MiReadPage currently cannot
3020 * handle non-cached streams.
3021 */
3022 Offset.QuadPart = 0;
3023 Status = ZwReadFile(FileHandle,
3024 NULL,
3025 NULL,
3026 NULL,
3027 &Iosb,
3028 &Buffer,
3029 sizeof (Buffer),
3030 &Offset,
3031 0);
3032 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
3033 {
3034 ObDereferenceObject(Section);
3035 ObDereferenceObject(FileObject);
3036 return(Status);
3037 }
3038 if (FileObject->SectionObjectPointer == NULL ||
3039 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3040 {
3041 /* FIXME: handle this situation */
3042 ObDereferenceObject(Section);
3043 ObDereferenceObject(FileObject);
3044 return STATUS_INVALID_PARAMETER;
3045 }
3046 }
3047
3048 /*
3049 * Lock the file
3050 */
3051 Status = MmspWaitForFileLock(FileObject);
3052 if (Status != STATUS_SUCCESS)
3053 {
3054 ObDereferenceObject(Section);
3055 ObDereferenceObject(FileObject);
3056 return(Status);
3057 }
3058
3059 /*
3060 * If this file hasn't been mapped as a data file before then allocate a
3061 * section segment to describe the data file mapping
3062 */
3063 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
3064 {
3065 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
3066 TAG_MM_SECTION_SEGMENT);
3067 if (Segment == NULL)
3068 {
3069 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3070 ObDereferenceObject(Section);
3071 ObDereferenceObject(FileObject);
3072 return(STATUS_NO_MEMORY);
3073 }
3074 Section->Segment = Segment;
3075 Segment->ReferenceCount = 1;
3076 ExInitializeFastMutex(&Segment->Lock);
3077 /*
3078 * Set the lock before assigning the segment to the file object
3079 */
3080 ExAcquireFastMutex(&Segment->Lock);
3081 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
3082
3083 Segment->Image.FileOffset = 0;
3084 Segment->Protection = SectionPageProtection;
3085 Segment->Flags = MM_DATAFILE_SEGMENT;
3086 Segment->Image.Characteristics = 0;
3087 Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
3088 if (AllocationAttributes & SEC_RESERVE)
3089 {
3090 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
3091 }
3092 else
3093 {
3094 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3095 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3096 }
3097 Segment->Image.VirtualAddress = 0;
3098 Segment->Locked = TRUE;
3099 MiInitializeSectionPageTable(Segment);
3100 }
3101 else
3102 {
3103 /*
3104 * If the file is already mapped as a data file then we may need
3105 * to extend it
3106 */
3107 Segment =
3108 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
3109 DataSectionObject;
3110 Section->Segment = Segment;
3111 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
3112 MmLockSectionSegment(Segment);
3113
3114 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
3115 !(AllocationAttributes & SEC_RESERVE))
3116 {
3117 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3118 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3119 }
3120 }
3121 MmUnlockSectionSegment(Segment);
3122 Section->FileObject = FileObject;
3123 Section->MaximumSize = MaximumSize;
3124 #ifndef NEWCC
3125 CcRosReferenceCache(FileObject);
3126 #endif
3127 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3128 *SectionObject = Section;
3129 return(STATUS_SUCCESS);
3130 }
3131
3132 /*
3133 TODO: not that great (declaring loaders statically, having to declare all of
3134 them, having to keep them extern, etc.), will fix in the future
3135 */
3136 extern NTSTATUS NTAPI PeFmtCreateSection
3137 (
3138 IN CONST VOID * FileHeader,
3139 IN SIZE_T FileHeaderSize,
3140 IN PVOID File,
3141 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3142 OUT PULONG Flags,
3143 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3144 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3145 );
3146
3147 extern NTSTATUS NTAPI ElfFmtCreateSection
3148 (
3149 IN CONST VOID * FileHeader,
3150 IN SIZE_T FileHeaderSize,
3151 IN PVOID File,
3152 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3153 OUT PULONG Flags,
3154 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3155 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3156 );
3157
3158 /* TODO: this is a standard DDK/PSDK macro */
3159 #ifndef RTL_NUMBER_OF
3160 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
3161 #endif
3162
3163 static PEXEFMT_LOADER ExeFmtpLoaders[] =
3164 {
3165 PeFmtCreateSection,
3166 #ifdef __ELF
3167 ElfFmtCreateSection
3168 #endif
3169 };
3170
3171 static
3172 PMM_SECTION_SEGMENT
3173 NTAPI
3174 ExeFmtpAllocateSegments(IN ULONG NrSegments)
3175 {
3176 SIZE_T SizeOfSegments;
3177 PMM_SECTION_SEGMENT Segments;
3178
3179 /* TODO: check for integer overflow */
3180 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
3181
3182 Segments = ExAllocatePoolWithTag(NonPagedPool,
3183 SizeOfSegments,
3184 TAG_MM_SECTION_SEGMENT);
3185
3186 if(Segments)
3187 RtlZeroMemory(Segments, SizeOfSegments);
3188
3189 return Segments;
3190 }
3191
3192 static
3193 NTSTATUS
3194 NTAPI
3195 ExeFmtpReadFile(IN PVOID File,
3196 IN PLARGE_INTEGER Offset,
3197 IN ULONG Length,
3198 OUT PVOID * Data,
3199 OUT PVOID * AllocBase,
3200 OUT PULONG ReadSize)
3201 {
3202 NTSTATUS Status;
3203 LARGE_INTEGER FileOffset;
3204 ULONG AdjustOffset;
3205 ULONG OffsetAdjustment;
3206 ULONG BufferSize;
3207 ULONG UsedSize;
3208 PVOID Buffer;
3209 PFILE_OBJECT FileObject = File;
3210 IO_STATUS_BLOCK Iosb;
3211
3212 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
3213
3214 if(Length == 0)
3215 {
3216 KeBugCheck(MEMORY_MANAGEMENT);
3217 }
3218
3219 FileOffset = *Offset;
3220
3221 /* Negative/special offset: it cannot be used in this context */
3222 if(FileOffset.u.HighPart < 0)
3223 {
3224 KeBugCheck(MEMORY_MANAGEMENT);
3225 }
3226
3227 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
3228 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
3229 FileOffset.u.LowPart = AdjustOffset;
3230
3231 BufferSize = Length + OffsetAdjustment;
3232 BufferSize = PAGE_ROUND_UP(BufferSize);
3233
3234 /* Flush data since we're about to perform a non-cached read */
3235 CcFlushCache(FileObject->SectionObjectPointer,
3236 &FileOffset,
3237 BufferSize,
3238 &Iosb);
3239
3240 /*
3241 * It's ok to use paged pool, because this is a temporary buffer only used in
3242 * the loading of executables. The assumption is that MmCreateSection is
3243 * always called at low IRQLs and that these buffers don't survive a brief
3244 * initialization phase
3245 */
3246 Buffer = ExAllocatePoolWithTag(PagedPool,
3247 BufferSize,
3248 'rXmM');
3249 if (!Buffer)
3250 {
3251 return STATUS_INSUFFICIENT_RESOURCES;
3252 }
3253
3254 UsedSize = 0;
3255
3256 Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
3257
3258 UsedSize = (ULONG)Iosb.Information;
3259
3260 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
3261 {
3262 Status = STATUS_IN_PAGE_ERROR;
3263 ASSERT(!NT_SUCCESS(Status));
3264 }
3265
3266 if(NT_SUCCESS(Status))
3267 {
3268 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
3269 *AllocBase = Buffer;
3270 *ReadSize = UsedSize - OffsetAdjustment;
3271 }
3272 else
3273 {
3274 ExFreePoolWithTag(Buffer, 'rXmM');
3275 }
3276
3277 return Status;
3278 }
3279
3280 #ifdef NASSERT
3281 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
3282 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
3283 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
3284 #else
3285 static
3286 VOID
3287 NTAPI
3288 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3289 {
3290 ULONG i;
3291
3292 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
3293 {
3294 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3295 ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
3296 }
3297 }
3298
3299 static
3300 VOID
3301 NTAPI
3302 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3303 {
3304 ULONG i;
3305
3306 MmspAssertSegmentsSorted(ImageSectionObject);
3307
3308 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3309 {
3310 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
3311
3312 if(i > 0)
3313 {
3314 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3315 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
3316 ImageSectionObject->Segments[i - 1].Length.QuadPart));
3317 }
3318 }
3319 }
3320
3321 static
3322 VOID
3323 NTAPI
3324 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3325 {
3326 ULONG i;
3327
3328 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3329 {
3330 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
3331 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
3332 }
3333 }
3334 #endif
3335
3336 static
3337 int
3338 __cdecl
3339 MmspCompareSegments(const void * x,
3340 const void * y)
3341 {
3342 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
3343 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
3344
3345 return
3346 (Segment1->Image.VirtualAddress - Segment2->Image.VirtualAddress) >>
3347 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
3348 }
3349
3350 /*
3351 * Ensures an image section's segments are sorted in memory
3352 */
3353 static
3354 VOID
3355 NTAPI
3356 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3357 IN ULONG Flags)
3358 {
3359 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
3360 {
3361 MmspAssertSegmentsSorted(ImageSectionObject);
3362 }
3363 else
3364 {
3365 qsort(ImageSectionObject->Segments,
3366 ImageSectionObject->NrSegments,
3367 sizeof(ImageSectionObject->Segments[0]),
3368 MmspCompareSegments);
3369 }
3370 }
3371
3372
3373 /*
3374 * Ensures an image section's segments don't overlap in memory and don't have
3375 * gaps and don't have a null size. We let them map to overlapping file regions,
3376 * though - that's not necessarily an error
3377 */
3378 static
3379 BOOLEAN
3380 NTAPI
3381 MmspCheckSegmentBounds
3382 (
3383 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3384 IN ULONG Flags
3385 )
3386 {
3387 ULONG i;
3388
3389 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
3390 {
3391 MmspAssertSegmentsNoOverlap(ImageSectionObject);
3392 return TRUE;
3393 }
3394
3395 ASSERT(ImageSectionObject->NrSegments >= 1);
3396
3397 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3398 {
3399 if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
3400 {
3401 return FALSE;
3402 }
3403
3404 if(i > 0)
3405 {
3406 /*
3407 * TODO: relax the limitation on gaps. For example, gaps smaller than a
3408 * page could be OK (Windows seems to be OK with them), and larger gaps
3409 * could lead to image sections spanning several discontiguous regions
3410 * (NtMapViewOfSection could then refuse to map them, and they could
3411 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
3412 */
3413 if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
3414 ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
3415 ImageSectionObject->Segments[i].Image.VirtualAddress)
3416 {
3417 return FALSE;
3418 }
3419 }
3420 }
3421
3422 return TRUE;
3423 }
3424
3425 /*
3426 * Merges and pads an image section's segments until they all are page-aligned
3427 * and have a size that is a multiple of the page size
3428 */
3429 static
3430 BOOLEAN
3431 NTAPI
3432 MmspPageAlignSegments
3433 (
3434 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3435 IN ULONG Flags
3436 )
3437 {
3438 ULONG i;
3439 ULONG LastSegment;
3440 PMM_SECTION_SEGMENT EffectiveSegment;
3441
3442 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
3443 {
3444 MmspAssertSegmentsPageAligned(ImageSectionObject);
3445 return TRUE;
3446 }
3447
3448 LastSegment = 0;
3449 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3450
3451 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3452 {
3453 /*
3454 * The first segment requires special handling
3455 */
3456 if (i == 0)
3457 {
3458 ULONG_PTR VirtualAddress;
3459 ULONG_PTR VirtualOffset;
3460
3461 VirtualAddress = EffectiveSegment->Image.VirtualAddress;
3462
3463 /* Round down the virtual address to the nearest page */
3464 EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
3465
3466 /* Round up the virtual size to the nearest page */
3467 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
3468 EffectiveSegment->Image.VirtualAddress;
3469
3470 /* Adjust the raw address and size */
3471 VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
3472
3473 if (EffectiveSegment->Image.FileOffset < VirtualOffset)
3474 {
3475 return FALSE;
3476 }
3477
3478 /*
3479 * Garbage in, garbage out: unaligned base addresses make the file
3480 * offset point in curious and odd places, but that's what we were
3481 * asked for
3482 */
3483 EffectiveSegment->Image.FileOffset -= VirtualOffset;
3484 EffectiveSegment->RawLength.QuadPart += VirtualOffset;
3485 }
3486 else
3487 {
3488 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
3489 ULONG_PTR EndOfEffectiveSegment;
3490
3491 EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
3492 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
3493
3494 /*
3495 * The current segment begins exactly where the current effective
3496 * segment ended, therefore beginning a new effective segment
3497 */
3498 if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
3499 {
3500 LastSegment ++;
3501 ASSERT(LastSegment <= i);
3502 ASSERT(LastSegment < ImageSectionObject->NrSegments);
3503
3504 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3505
3506 if (LastSegment != i)
3507 {
3508 /*
3509 * Copy the current segment. If necessary, the effective segment
3510 * will be expanded later
3511 */
3512 *EffectiveSegment = *Segment;
3513 }
3514
3515 /*
3516 * Page-align the virtual size. We know for sure the virtual address
3517 * already is
3518 */
3519 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
3520 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
3521 }
3522 /*
3523 * The current segment is still part of the current effective segment:
3524 * extend the effective segment to reflect this
3525 */
3526 else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
3527 {
3528 static const ULONG FlagsToProtection[16] =
3529 {
3530 PAGE_NOACCESS,
3531 PAGE_READONLY,
3532 PAGE_READWRITE,
3533 PAGE_READWRITE,
3534 PAGE_EXECUTE_READ,
3535 PAGE_EXECUTE_READ,
3536 PAGE_EXECUTE_READWRITE,
3537 PAGE_EXECUTE_READWRITE,
3538 PAGE_WRITECOPY,
3539 PAGE_WRITECOPY,
3540 PAGE_WRITECOPY,
3541 PAGE_WRITECOPY,
3542 PAGE_EXECUTE_WRITECOPY,
3543 PAGE_EXECUTE_WRITECOPY,
3544 PAGE_EXECUTE_WRITECOPY,
3545 PAGE_EXECUTE_WRITECOPY
3546 };
3547
3548 unsigned ProtectionFlags;
3549
3550 /*
3551 * Extend the file size
3552 */
3553
3554 /* Unaligned segments must be contiguous within the file */
3555 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
3556 EffectiveSegment->RawLength.QuadPart))
3557 {
3558 return FALSE;
3559 }
3560
3561 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
3562
3563 /*
3564 * Extend the virtual size
3565 */
3566 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
3567
3568 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
3569 EffectiveSegment->Image.VirtualAddress;
3570
3571 /*
3572 * Merge the protection
3573 */
3574 EffectiveSegment->Protection |= Segment->Protection;
3575
3576 /* Clean up redundance */
3577 ProtectionFlags = 0;
3578
3579 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
3580 ProtectionFlags |= 1 << 0;
3581
3582 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
3583 ProtectionFlags |= 1 << 1;
3584
3585 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
3586 ProtectionFlags |= 1 << 2;
3587
3588 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3589 ProtectionFlags |= 1 << 3;
3590
3591 ASSERT(ProtectionFlags < 16);
3592 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3593
3594 /* If a segment was required to be shared and cannot, fail */
3595 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3596 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3597 {
3598 return FALSE;
3599 }
3600 }
3601 /*
3602 * We assume no holes between segments at this point
3603 */
3604 else
3605 {
3606 KeBugCheck(MEMORY_MANAGEMENT);
3607 }
3608 }
3609 }
3610 ImageSectionObject->NrSegments = LastSegment + 1;
3611
3612 return TRUE;
3613 }
3614
3615 NTSTATUS
3616 ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,
3617 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3618 {
3619 LARGE_INTEGER Offset;
3620 PVOID FileHeader;
3621 PVOID FileHeaderBuffer;
3622 ULONG FileHeaderSize;
3623 ULONG Flags;
3624 ULONG OldNrSegments;
3625 NTSTATUS Status;
3626 ULONG i;
3627
3628 /*
3629 * Read the beginning of the file (2 pages). Should be enough to contain
3630 * all (or most) of the headers
3631 */
3632 Offset.QuadPart = 0;
3633
3634 Status = ExeFmtpReadFile (FileObject,
3635 &Offset,
3636 PAGE_SIZE * 2,
3637 &FileHeader,
3638 &FileHeaderBuffer,
3639 &FileHeaderSize);
3640
3641 if (!NT_SUCCESS(Status))
3642 return Status;
3643
3644 if (FileHeaderSize == 0)
3645 {
3646 ExFreePool(FileHeaderBuffer);
3647 return STATUS_UNSUCCESSFUL;
3648 }
3649
3650 /*
3651 * Look for a loader that can handle this executable
3652 */
3653 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3654 {
3655 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3656 Flags = 0;
3657
3658 Status = ExeFmtpLoaders[i](FileHeader,
3659 FileHeaderSize,
3660 FileObject,
3661 ImageSectionObject,
3662 &Flags,
3663 ExeFmtpReadFile,
3664 ExeFmtpAllocateSegments);
3665
3666 if (!NT_SUCCESS(Status))
3667 {
3668 if (ImageSectionObject->Segments)
3669 {
3670 ExFreePool(ImageSectionObject->Segments);
3671 ImageSectionObject->Segments = NULL;
3672 }
3673 }
3674
3675 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3676 break;
3677 }
3678
3679 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3680
3681 /*
3682 * No loader handled the format
3683 */
3684 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3685 {
3686 Status = STATUS_INVALID_IMAGE_NOT_MZ;
3687 ASSERT(!NT_SUCCESS(Status));
3688 }
3689
3690 if (!NT_SUCCESS(Status))
3691 return Status;
3692
3693 ASSERT(ImageSectionObject->Segments != NULL);
3694
3695 /*
3696 * Some defaults
3697 */
3698 /* FIXME? are these values platform-dependent? */
3699 if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3700 ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3701
3702 if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3703 ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3704
3705 if(ImageSectionObject->BasedAddress == NULL)
3706 {
3707 if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3708 ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3709 else
3710 ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3711 }
3712
3713 /*
3714 * And now the fun part: fixing the segments
3715 */
3716
3717 /* Sort them by virtual address */
3718 MmspSortSegments(ImageSectionObject, Flags);
3719
3720 /* Ensure they don't overlap in memory */
3721 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3722 return STATUS_INVALID_IMAGE_FORMAT;
3723
3724 /* Ensure they are aligned */
3725 OldNrSegments = ImageSectionObject->NrSegments;
3726
3727 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3728 return STATUS_INVALID_IMAGE_FORMAT;
3729
3730 /* Trim them if the alignment phase merged some of them */
3731 if (ImageSectionObject->NrSegments < OldNrSegments)
3732 {
3733 PMM_SECTION_SEGMENT Segments;
3734 SIZE_T SizeOfSegments;
3735
3736 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3737
3738 Segments = ExAllocatePoolWithTag(PagedPool,
3739 SizeOfSegments,
3740 TAG_MM_SECTION_SEGMENT);
3741
3742 if (Segments == NULL)
3743 return STATUS_INSUFFICIENT_RESOURCES;
3744
3745 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3746 ExFreePool(ImageSectionObject->Segments);
3747 ImageSectionObject->Segments = Segments;
3748 }
3749
3750 /* And finish their initialization */
3751 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3752 {
3753 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3754 ImageSectionObject->Segments[i].ReferenceCount = 1;
3755 MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3756 }
3757
3758 ASSERT(NT_SUCCESS(Status));
3759 return Status;
3760 }
3761
3762 NTSTATUS
3763 MmCreateImageSection(PROS_SECTION_OBJECT *SectionObject,
3764 ACCESS_MASK DesiredAccess,
3765 POBJECT_ATTRIBUTES ObjectAttributes,
3766 PLARGE_INTEGER UMaximumSize,
3767 ULONG SectionPageProtection,
3768 ULONG AllocationAttributes,
3769 PFILE_OBJECT FileObject)
3770 {
3771 PROS_SECTION_OBJECT Section;
3772 NTSTATUS Status;
3773 PMM_SECTION_SEGMENT SectionSegments;
3774 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3775 ULONG i;
3776
3777 if (FileObject == NULL)
3778 return STATUS_INVALID_FILE_FOR_SECTION;
3779
3780 #ifndef NEWCC
3781 if (FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3782 {
3783 DPRINT1("Denying section creation due to missing cache initialization\n");
3784 return STATUS_INVALID_FILE_FOR_SECTION;
3785 }
3786 #endif
3787
3788 /*
3789 * Create the section
3790 */
3791 Status = ObCreateObject (ExGetPreviousMode(),
3792 MmSectionObjectType,
3793 ObjectAttributes,
3794 ExGetPreviousMode(),
3795 NULL,
3796 sizeof(ROS_SECTION_OBJECT),
3797 0,
3798 0,
3799 (PVOID*)(PVOID)&Section);
3800 if (!NT_SUCCESS(Status))
3801 {
3802 ObDereferenceObject(FileObject);
3803 return(Status);
3804 }
3805
3806 /*
3807 * Initialize it
3808 */
3809 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
3810 Section->Type = 'SC';
3811 Section->Size = 'TN';
3812 Section->SectionPageProtection = SectionPageProtection;
3813 Section->AllocationAttributes = AllocationAttributes;
3814
3815 #ifndef NEWCC
3816 /*
3817 * Initialized caching for this file object if previously caching
3818 * was initialized for the same on disk file
3819 */
3820 Status = CcTryToInitializeFileCache(FileObject);
3821 #else
3822 Status = STATUS_SUCCESS;
3823 #endif
3824
3825 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)
3826 {
3827 NTSTATUS StatusExeFmt;
3828
3829 ImageSectionObject = ExAllocatePoolWithTag(PagedPool, sizeof(MM_IMAGE_SECTION_OBJECT), TAG_MM_SECTION_SEGMENT);
3830 if (ImageSectionObject == NULL)
3831 {
3832 ObDereferenceObject(FileObject);
3833 ObDereferenceObject(Section);
3834 return(STATUS_NO_MEMORY);
3835 }
3836
3837 RtlZeroMemory(ImageSectionObject, sizeof<