4943dabae3126fec07a23cb860302b072fd25fd5
[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 KeEnterCriticalRegion();
1114 Status = CcRosGetVacb(SharedCacheMap,
1115 FileOffset,
1116 &BaseOffset,
1117 &BaseAddress,
1118 &UptoDate,
1119 &Vacb);
1120 if (!NT_SUCCESS(Status))
1121 {
1122 KeLeaveCriticalRegion();
1123 return(Status);
1124 }
1125 if (!UptoDate)
1126 {
1127 /*
1128 * If the VACB isn't up to date then call the file
1129 * system to read in the data.
1130 */
1131 Status = CcReadVirtualAddress(Vacb);
1132 if (!NT_SUCCESS(Status))
1133 {
1134 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1135 KeLeaveCriticalRegion();
1136 return Status;
1137 }
1138 }
1139
1140 /* Probe the page, since it's PDE might not be synced */
1141 (void)*((volatile char*)BaseAddress + FileOffset - BaseOffset);
1142
1143 /*
1144 * Retrieve the page from the view that we actually want.
1145 */
1146 (*Page) = MmGetPhysicalAddress((char*)BaseAddress +
1147 FileOffset - BaseOffset).LowPart >> PAGE_SHIFT;
1148
1149 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, TRUE);
1150 KeLeaveCriticalRegion();
1151 }
1152 else
1153 {
1154 PEPROCESS Process;
1155 KIRQL Irql;
1156 PVOID PageAddr;
1157 LONGLONG VacbOffset;
1158
1159 /*
1160 * Allocate a page, this is rather complicated by the possibility
1161 * we might have to move other things out of memory
1162 */
1163 MI_SET_USAGE(MI_USAGE_SECTION);
1164 MI_SET_PROCESS2(PsGetCurrentProcess()->ImageFileName);
1165 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, Page);
1166 if (!NT_SUCCESS(Status))
1167 {
1168 return(Status);
1169 }
1170 KeEnterCriticalRegion();
1171 Status = CcRosGetVacb(SharedCacheMap,
1172 FileOffset,
1173 &BaseOffset,
1174 &BaseAddress,
1175 &UptoDate,
1176 &Vacb);
1177 if (!NT_SUCCESS(Status))
1178 {
1179 KeLeaveCriticalRegion();
1180 return(Status);
1181 }
1182 if (!UptoDate)
1183 {
1184 /*
1185 * If the VACB isn't up to date then call the file
1186 * system to read in the data.
1187 */
1188 Status = CcReadVirtualAddress(Vacb);
1189 if (!NT_SUCCESS(Status))
1190 {
1191 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1192 KeLeaveCriticalRegion();
1193 return Status;
1194 }
1195 }
1196
1197 Process = PsGetCurrentProcess();
1198 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1199 VacbOffset = BaseOffset + VACB_MAPPING_GRANULARITY - FileOffset;
1200 Length = RawLength - SegOffset;
1201 if (Length <= VacbOffset && Length <= PAGE_SIZE)
1202 {
1203 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, Length);
1204 }
1205 else if (VacbOffset >= PAGE_SIZE)
1206 {
1207 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, PAGE_SIZE);
1208 }
1209 else
1210 {
1211 memcpy(PageAddr, (char*)BaseAddress + FileOffset - BaseOffset, VacbOffset);
1212 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1213 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
1214 Status = CcRosGetVacb(SharedCacheMap,
1215 FileOffset + VacbOffset,
1216 &BaseOffset,
1217 &BaseAddress,
1218 &UptoDate,
1219 &Vacb);
1220 if (!NT_SUCCESS(Status))
1221 {
1222 KeLeaveCriticalRegion();
1223 return(Status);
1224 }
1225 if (!UptoDate)
1226 {
1227 /*
1228 * If the VACB isn't up to date then call the file
1229 * system to read in the data.
1230 */
1231 Status = CcReadVirtualAddress(Vacb);
1232 if (!NT_SUCCESS(Status))
1233 {
1234 CcRosReleaseVacb(SharedCacheMap, Vacb, FALSE, FALSE, FALSE);
1235 KeLeaveCriticalRegion();
1236 return Status;
1237 }
1238 }
1239 PageAddr = MiMapPageInHyperSpace(Process, *Page, &Irql);
1240 if (Length < PAGE_SIZE)
1241 {
1242 memcpy((char*)PageAddr + VacbOffset, BaseAddress, Length - VacbOffset);
1243 }
1244 else
1245 {
1246 memcpy((char*)PageAddr + VacbOffset, BaseAddress, PAGE_SIZE - VacbOffset);
1247 }
1248 }
1249 MiUnmapPageInHyperSpace(Process, PageAddr, Irql);
1250 CcRosReleaseVacb(SharedCacheMap, Vacb, TRUE, FALSE, FALSE);
1251 KeLeaveCriticalRegion();
1252 }
1253 return(STATUS_SUCCESS);
1254 }
1255 #else
1256 NTSTATUS
1257 NTAPI
1258 MiReadPage(PMEMORY_AREA MemoryArea,
1259 LONGLONG SegOffset,
1260 PPFN_NUMBER Page)
1261 /*
1262 * FUNCTION: Read a page for a section backed memory area.
1263 * PARAMETERS:
1264 * MemoryArea - Memory area to read the page for.
1265 * Offset - Offset of the page to read.
1266 * Page - Variable that receives a page contains the read data.
1267 */
1268 {
1269 MM_REQUIRED_RESOURCES Resources;
1270 NTSTATUS Status;
1271
1272 RtlZeroMemory(&Resources, sizeof(MM_REQUIRED_RESOURCES));
1273
1274 Resources.Context = MemoryArea->Data.SectionData.Section->FileObject;
1275 Resources.FileOffset.QuadPart = SegOffset +
1276 MemoryArea->Data.SectionData.Segment->Image.FileOffset;
1277 Resources.Consumer = MC_USER;
1278 Resources.Amount = PAGE_SIZE;
1279
1280 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]);
1281
1282 Status = MiReadFilePage(MmGetKernelAddressSpace(), MemoryArea, &Resources);
1283 *Page = Resources.Page[0];
1284 return Status;
1285 }
1286 #endif
1287
1288 NTSTATUS
1289 NTAPI
1290 MmNotPresentFaultSectionView(PMMSUPPORT AddressSpace,
1291 MEMORY_AREA* MemoryArea,
1292 PVOID Address,
1293 BOOLEAN Locked)
1294 {
1295 LARGE_INTEGER Offset;
1296 PFN_NUMBER Page;
1297 NTSTATUS Status;
1298 PROS_SECTION_OBJECT Section;
1299 PMM_SECTION_SEGMENT Segment;
1300 ULONG_PTR Entry;
1301 ULONG_PTR Entry1;
1302 ULONG Attributes;
1303 PMM_REGION Region;
1304 BOOLEAN HasSwapEntry;
1305 PVOID PAddress;
1306 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1307 SWAPENTRY SwapEntry;
1308
1309 /*
1310 * There is a window between taking the page fault and locking the
1311 * address space when another thread could load the page so we check
1312 * that.
1313 */
1314 if (MmIsPagePresent(Process, Address))
1315 {
1316 return(STATUS_SUCCESS);
1317 }
1318
1319 if (MmIsDisabledPage(Process, Address))
1320 {
1321 return(STATUS_ACCESS_VIOLATION);
1322 }
1323
1324 /*
1325 * Check for the virtual memory area being deleted.
1326 */
1327 if (MemoryArea->DeleteInProgress)
1328 {
1329 return(STATUS_UNSUCCESSFUL);
1330 }
1331
1332 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1333 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1334 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1335
1336 Segment = MemoryArea->Data.SectionData.Segment;
1337 Section = MemoryArea->Data.SectionData.Section;
1338 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1339 &MemoryArea->Data.SectionData.RegionListHead,
1340 Address, NULL);
1341 ASSERT(Region != NULL);
1342 /*
1343 * Lock the segment
1344 */
1345 MmLockSectionSegment(Segment);
1346 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1347 /*
1348 * Check if this page needs to be mapped COW
1349 */
1350 if ((Segment->WriteCopy) &&
1351 (Region->Protect == PAGE_READWRITE ||
1352 Region->Protect == PAGE_EXECUTE_READWRITE))
1353 {
1354 Attributes = Region->Protect == PAGE_READWRITE ? PAGE_READONLY : PAGE_EXECUTE_READ;
1355 }
1356 else
1357 {
1358 Attributes = Region->Protect;
1359 }
1360
1361 /*
1362 * Check if someone else is already handling this fault, if so wait
1363 * for them
1364 */
1365 if (Entry && IS_SWAP_FROM_SSE(Entry) && SWAPENTRY_FROM_SSE(Entry) == MM_WAIT_ENTRY)
1366 {
1367 MmUnlockSectionSegment(Segment);
1368 MmUnlockAddressSpace(AddressSpace);
1369 MiWaitForPageEvent(NULL, NULL);
1370 MmLockAddressSpace(AddressSpace);
1371 DPRINT("Address 0x%p\n", Address);
1372 return(STATUS_MM_RESTART_OPERATION);
1373 }
1374
1375 HasSwapEntry = MmIsPageSwapEntry(Process, Address);
1376
1377 if (HasSwapEntry)
1378 {
1379 SWAPENTRY DummyEntry;
1380
1381 /*
1382 * Is it a wait entry?
1383 */
1384 MmGetPageFileMapping(Process, Address, &SwapEntry);
1385
1386 if (SwapEntry == MM_WAIT_ENTRY)
1387 {
1388 MmUnlockSectionSegment(Segment);
1389 MmUnlockAddressSpace(AddressSpace);
1390 MiWaitForPageEvent(NULL, NULL);
1391 MmLockAddressSpace(AddressSpace);
1392 return STATUS_MM_RESTART_OPERATION;
1393 }
1394
1395 /*
1396 * Must be private page we have swapped out.
1397 */
1398
1399 /*
1400 * Sanity check
1401 */
1402 if (Segment->Flags & MM_PAGEFILE_SEGMENT)
1403 {
1404 DPRINT1("Found a swaped out private page in a pagefile section.\n");
1405 KeBugCheck(MEMORY_MANAGEMENT);
1406 }
1407
1408 MmUnlockSectionSegment(Segment);
1409 MmDeletePageFileMapping(Process, Address, &SwapEntry);
1410 MmCreatePageFileMapping(Process, Address, MM_WAIT_ENTRY);
1411
1412 MmUnlockAddressSpace(AddressSpace);
1413 MI_SET_USAGE(MI_USAGE_SECTION);
1414 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1415 if (!Process) MI_SET_PROCESS2("Kernel Section");
1416 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1417 if (!NT_SUCCESS(Status))
1418 {
1419 KeBugCheck(MEMORY_MANAGEMENT);
1420 }
1421
1422 Status = MmReadFromSwapPage(SwapEntry, Page);
1423 if (!NT_SUCCESS(Status))
1424 {
1425 DPRINT1("MmReadFromSwapPage failed, status = %x\n", Status);
1426 KeBugCheck(MEMORY_MANAGEMENT);
1427 }
1428 MmLockAddressSpace(AddressSpace);
1429 MmDeletePageFileMapping(Process, PAddress, &DummyEntry);
1430 Status = MmCreateVirtualMapping(Process,
1431 PAddress,
1432 Region->Protect,
1433 &Page,
1434 1);
1435 if (!NT_SUCCESS(Status))
1436 {
1437 DPRINT("MmCreateVirtualMapping failed, not out of memory\n");
1438 KeBugCheck(MEMORY_MANAGEMENT);
1439 return(Status);
1440 }
1441
1442 /*
1443 * Store the swap entry for later use.
1444 */
1445 MmSetSavedSwapEntryPage(Page, SwapEntry);
1446
1447 /*
1448 * Add the page to the process's working set
1449 */
1450 MmInsertRmap(Page, Process, Address);
1451 /*
1452 * Finish the operation
1453 */
1454 MiSetPageEvent(Process, Address);
1455 DPRINT("Address 0x%p\n", Address);
1456 return(STATUS_SUCCESS);
1457 }
1458
1459 /*
1460 * Satisfying a page fault on a map of /Device/PhysicalMemory is easy
1461 */
1462 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1463 {
1464 MmUnlockSectionSegment(Segment);
1465 /*
1466 * Just map the desired physical page
1467 */
1468 Page = (PFN_NUMBER)(Offset.QuadPart >> PAGE_SHIFT);
1469 Status = MmCreateVirtualMappingUnsafe(Process,
1470 PAddress,
1471 Region->Protect,
1472 &Page,
1473 1);
1474 if (!NT_SUCCESS(Status))
1475 {
1476 DPRINT("MmCreateVirtualMappingUnsafe failed, not out of memory\n");
1477 KeBugCheck(MEMORY_MANAGEMENT);
1478 return(Status);
1479 }
1480
1481 /*
1482 * Cleanup and release locks
1483 */
1484 MiSetPageEvent(Process, Address);
1485 DPRINT("Address 0x%p\n", Address);
1486 return(STATUS_SUCCESS);
1487 }
1488
1489 /*
1490 * Get the entry corresponding to the offset within the section
1491 */
1492 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1493
1494 if (Entry == 0)
1495 {
1496 SWAPENTRY FakeSwapEntry;
1497
1498 /*
1499 * If the entry is zero (and it can't change because we have
1500 * locked the segment) then we need to load the page.
1501 */
1502
1503 /*
1504 * Release all our locks and read in the page from disk
1505 */
1506 MmSetPageEntrySectionSegment(Segment, &Offset, MAKE_SWAP_SSE(MM_WAIT_ENTRY));
1507 MmUnlockSectionSegment(Segment);
1508 MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY);
1509 MmUnlockAddressSpace(AddressSpace);
1510
1511 if ((Segment->Flags & MM_PAGEFILE_SEGMENT) ||
1512 ((Offset.QuadPart >= (LONGLONG)PAGE_ROUND_UP(Segment->RawLength.QuadPart) &&
1513 (Section->AllocationAttributes & SEC_IMAGE))))
1514 {
1515 MI_SET_USAGE(MI_USAGE_SECTION);
1516 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1517 if (!Process) MI_SET_PROCESS2("Kernel Section");
1518 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1519 if (!NT_SUCCESS(Status))
1520 {
1521 DPRINT1("MmRequestPageMemoryConsumer failed (Status %x)\n", Status);
1522 }
1523
1524 }
1525 else
1526 {
1527 Status = MiReadPage(MemoryArea, Offset.QuadPart, &Page);
1528 if (!NT_SUCCESS(Status))
1529 {
1530 DPRINT1("MiReadPage failed (Status %x)\n", Status);
1531 }
1532 }
1533 if (!NT_SUCCESS(Status))
1534 {
1535 /*
1536 * FIXME: What do we know in this case?
1537 */
1538 /*
1539 * Cleanup and release locks
1540 */
1541 MmLockAddressSpace(AddressSpace);
1542 MiSetPageEvent(Process, Address);
1543 DPRINT("Address 0x%p\n", Address);
1544 return(Status);
1545 }
1546
1547 /*
1548 * Mark the offset within the section as having valid, in-memory
1549 * data
1550 */
1551 MmLockAddressSpace(AddressSpace);
1552 MmLockSectionSegment(Segment);
1553 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1554 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1555 MmUnlockSectionSegment(Segment);
1556
1557 MmDeletePageFileMapping(Process, PAddress, &FakeSwapEntry);
1558 DPRINT("CreateVirtualMapping Page %x Process %p PAddress %p Attributes %x\n",
1559 Page, Process, PAddress, Attributes);
1560 Status = MmCreateVirtualMapping(Process,
1561 PAddress,
1562 Attributes,
1563 &Page,
1564 1);
1565 if (!NT_SUCCESS(Status))
1566 {
1567 DPRINT1("Unable to create virtual mapping\n");
1568 KeBugCheck(MEMORY_MANAGEMENT);
1569 }
1570 ASSERT(MmIsPagePresent(Process, PAddress));
1571 MmInsertRmap(Page, Process, Address);
1572
1573 MiSetPageEvent(Process, Address);
1574 DPRINT("Address 0x%p\n", Address);
1575 return(STATUS_SUCCESS);
1576 }
1577 else if (IS_SWAP_FROM_SSE(Entry))
1578 {
1579 SWAPENTRY SwapEntry;
1580
1581 SwapEntry = SWAPENTRY_FROM_SSE(Entry);
1582
1583 /*
1584 * Release all our locks and read in the page from disk
1585 */
1586 MmUnlockSectionSegment(Segment);
1587
1588 MmUnlockAddressSpace(AddressSpace);
1589 MI_SET_USAGE(MI_USAGE_SECTION);
1590 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1591 if (!Process) MI_SET_PROCESS2("Kernel Section");
1592 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &Page);
1593 if (!NT_SUCCESS(Status))
1594 {
1595 KeBugCheck(MEMORY_MANAGEMENT);
1596 }
1597
1598 Status = MmReadFromSwapPage(SwapEntry, Page);
1599 if (!NT_SUCCESS(Status))
1600 {
1601 KeBugCheck(MEMORY_MANAGEMENT);
1602 }
1603
1604 /*
1605 * Relock the address space and segment
1606 */
1607 MmLockAddressSpace(AddressSpace);
1608 MmLockSectionSegment(Segment);
1609
1610 /*
1611 * Check the entry. No one should change the status of a page
1612 * that has a pending page-in.
1613 */
1614 Entry1 = MmGetPageEntrySectionSegment(Segment, &Offset);
1615 if (Entry != Entry1)
1616 {
1617 DPRINT1("Someone changed ppte entry while we slept (%x vs %x)\n", Entry, Entry1);
1618 KeBugCheck(MEMORY_MANAGEMENT);
1619 }
1620
1621 /*
1622 * Mark the offset within the section as having valid, in-memory
1623 * data
1624 */
1625 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
1626 MmSetPageEntrySectionSegment(Segment, &Offset, Entry);
1627 MmUnlockSectionSegment(Segment);
1628
1629 /*
1630 * Save the swap entry.
1631 */
1632 MmSetSavedSwapEntryPage(Page, SwapEntry);
1633 Status = MmCreateVirtualMapping(Process,
1634 PAddress,
1635 Region->Protect,
1636 &Page,
1637 1);
1638 if (!NT_SUCCESS(Status))
1639 {
1640 DPRINT1("Unable to create virtual mapping\n");
1641 KeBugCheck(MEMORY_MANAGEMENT);
1642 }
1643 MmInsertRmap(Page, Process, Address);
1644 MiSetPageEvent(Process, Address);
1645 DPRINT("Address 0x%p\n", Address);
1646 return(STATUS_SUCCESS);
1647 }
1648 else
1649 {
1650 /*
1651 * If the section offset is already in-memory and valid then just
1652 * take another reference to the page
1653 */
1654
1655 Page = PFN_FROM_SSE(Entry);
1656
1657 MmSharePageEntrySectionSegment(Segment, &Offset);
1658 MmUnlockSectionSegment(Segment);
1659
1660 Status = MmCreateVirtualMapping(Process,
1661 PAddress,
1662 Attributes,
1663 &Page,
1664 1);
1665 if (!NT_SUCCESS(Status))
1666 {
1667 DPRINT1("Unable to create virtual mapping\n");
1668 KeBugCheck(MEMORY_MANAGEMENT);
1669 }
1670 MmInsertRmap(Page, Process, Address);
1671 MiSetPageEvent(Process, Address);
1672 DPRINT("Address 0x%p\n", Address);
1673 return(STATUS_SUCCESS);
1674 }
1675 }
1676
1677 NTSTATUS
1678 NTAPI
1679 MmAccessFaultSectionView(PMMSUPPORT AddressSpace,
1680 MEMORY_AREA* MemoryArea,
1681 PVOID Address)
1682 {
1683 PMM_SECTION_SEGMENT Segment;
1684 PROS_SECTION_OBJECT Section;
1685 PFN_NUMBER OldPage;
1686 PFN_NUMBER NewPage;
1687 NTSTATUS Status;
1688 PVOID PAddress;
1689 LARGE_INTEGER Offset;
1690 PMM_REGION Region;
1691 ULONG_PTR Entry;
1692 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1693 SWAPENTRY SwapEntry;
1694
1695 DPRINT("MmAccessFaultSectionView(%p, %p, %p)\n", AddressSpace, MemoryArea, Address);
1696
1697 /*
1698 * Check if the page has already been set readwrite
1699 */
1700 if (MmGetPageProtect(Process, Address) & PAGE_READWRITE)
1701 {
1702 DPRINT("Address 0x%p\n", Address);
1703 return(STATUS_SUCCESS);
1704 }
1705
1706 /*
1707 * Find the offset of the page
1708 */
1709 PAddress = MM_ROUND_DOWN(Address, PAGE_SIZE);
1710 Offset.QuadPart = (ULONG_PTR)PAddress - MA_GetStartingAddress(MemoryArea)
1711 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1712
1713 Segment = MemoryArea->Data.SectionData.Segment;
1714 Section = MemoryArea->Data.SectionData.Section;
1715 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
1716 &MemoryArea->Data.SectionData.RegionListHead,
1717 Address, NULL);
1718 ASSERT(Region != NULL);
1719 /*
1720 * Lock the segment
1721 */
1722 MmLockSectionSegment(Segment);
1723
1724 OldPage = MmGetPfnForProcess(Process, Address);
1725 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1726
1727 MmUnlockSectionSegment(Segment);
1728
1729 /*
1730 * Check if we are doing COW
1731 */
1732 if (!((Segment->WriteCopy) &&
1733 (Region->Protect == PAGE_READWRITE ||
1734 Region->Protect == PAGE_EXECUTE_READWRITE)))
1735 {
1736 DPRINT("Address 0x%p\n", Address);
1737 return(STATUS_ACCESS_VIOLATION);
1738 }
1739
1740 if (IS_SWAP_FROM_SSE(Entry) ||
1741 PFN_FROM_SSE(Entry) != OldPage)
1742 {
1743 /* This is a private page. We must only change the page protection. */
1744 MmSetPageProtect(Process, Address, Region->Protect);
1745 return(STATUS_SUCCESS);
1746 }
1747
1748 if(OldPage == 0)
1749 DPRINT("OldPage == 0!\n");
1750
1751 /*
1752 * Get or create a pageop
1753 */
1754 MmLockSectionSegment(Segment);
1755 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
1756
1757 /*
1758 * Wait for any other operations to complete
1759 */
1760 if (Entry == SWAPENTRY_FROM_SSE(MM_WAIT_ENTRY))
1761 {
1762 MmUnlockSectionSegment(Segment);
1763 MmUnlockAddressSpace(AddressSpace);
1764 MiWaitForPageEvent(NULL, NULL);
1765 /*
1766 * Restart the operation
1767 */
1768 MmLockAddressSpace(AddressSpace);
1769 DPRINT("Address 0x%p\n", Address);
1770 return(STATUS_MM_RESTART_OPERATION);
1771 }
1772
1773 MmDeleteRmap(OldPage, Process, PAddress);
1774 MmDeleteVirtualMapping(Process, PAddress, NULL, NULL);
1775 MmCreatePageFileMapping(Process, PAddress, MM_WAIT_ENTRY);
1776
1777 /*
1778 * Release locks now we have the pageop
1779 */
1780 MmUnlockSectionSegment(Segment);
1781 MmUnlockAddressSpace(AddressSpace);
1782
1783 /*
1784 * Allocate a page
1785 */
1786 MI_SET_USAGE(MI_USAGE_SECTION);
1787 if (Process) MI_SET_PROCESS2(Process->ImageFileName);
1788 if (!Process) MI_SET_PROCESS2("Kernel Section");
1789 Status = MmRequestPageMemoryConsumer(MC_USER, TRUE, &NewPage);
1790 if (!NT_SUCCESS(Status))
1791 {
1792 KeBugCheck(MEMORY_MANAGEMENT);
1793 }
1794
1795 /*
1796 * Copy the old page
1797 */
1798 MiCopyFromUserPage(NewPage, OldPage);
1799
1800 MmLockAddressSpace(AddressSpace);
1801
1802 /*
1803 * Set the PTE to point to the new page
1804 */
1805 MmDeletePageFileMapping(Process, PAddress, &SwapEntry);
1806 Status = MmCreateVirtualMapping(Process,
1807 PAddress,
1808 Region->Protect,
1809 &NewPage,
1810 1);
1811 if (!NT_SUCCESS(Status))
1812 {
1813 DPRINT1("MmCreateVirtualMapping failed, unable to create virtual mapping, not out of memory\n");
1814 KeBugCheck(MEMORY_MANAGEMENT);
1815 return(Status);
1816 }
1817
1818 /*
1819 * Unshare the old page.
1820 */
1821 DPRINT("Swapping page (Old %x New %x)\n", OldPage, NewPage);
1822 MmInsertRmap(NewPage, Process, PAddress);
1823 MmLockSectionSegment(Segment);
1824 MmUnsharePageEntrySectionSegment(Section, Segment, &Offset, FALSE, FALSE, NULL);
1825 MmUnlockSectionSegment(Segment);
1826
1827 MiSetPageEvent(Process, Address);
1828 DPRINT("Address 0x%p\n", Address);
1829 return(STATUS_SUCCESS);
1830 }
1831
1832 VOID
1833 MmPageOutDeleteMapping(PVOID Context, PEPROCESS Process, PVOID Address)
1834 {
1835 MM_SECTION_PAGEOUT_CONTEXT* PageOutContext;
1836 BOOLEAN WasDirty;
1837 PFN_NUMBER Page = 0;
1838
1839 PageOutContext = (MM_SECTION_PAGEOUT_CONTEXT*)Context;
1840 if (Process)
1841 {
1842 MmLockAddressSpace(&Process->Vm);
1843 }
1844
1845 MmDeleteVirtualMapping(Process,
1846 Address,
1847 &WasDirty,
1848 &Page);
1849 if (WasDirty)
1850 {
1851 PageOutContext->WasDirty = TRUE;
1852 }
1853 if (!PageOutContext->Private)
1854 {
1855 MmLockSectionSegment(PageOutContext->Segment);
1856 MmUnsharePageEntrySectionSegment((PROS_SECTION_OBJECT)PageOutContext->Section,
1857 PageOutContext->Segment,
1858 &PageOutContext->Offset,
1859 PageOutContext->WasDirty,
1860 TRUE,
1861 &PageOutContext->SectionEntry);
1862 MmUnlockSectionSegment(PageOutContext->Segment);
1863 }
1864 if (Process)
1865 {
1866 MmUnlockAddressSpace(&Process->Vm);
1867 }
1868
1869 if (PageOutContext->Private)
1870 {
1871 MmReleasePageMemoryConsumer(MC_USER, Page);
1872 }
1873 }
1874
1875 NTSTATUS
1876 NTAPI
1877 MmPageOutSectionView(PMMSUPPORT AddressSpace,
1878 MEMORY_AREA* MemoryArea,
1879 PVOID Address, ULONG_PTR Entry)
1880 {
1881 PFN_NUMBER Page;
1882 MM_SECTION_PAGEOUT_CONTEXT Context;
1883 SWAPENTRY SwapEntry;
1884 NTSTATUS Status;
1885 #ifndef NEWCC
1886 ULONGLONG FileOffset;
1887 PFILE_OBJECT FileObject;
1888 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
1889 BOOLEAN IsImageSection;
1890 #endif
1891 BOOLEAN DirectMapped;
1892 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
1893 KIRQL OldIrql;
1894
1895 Address = (PVOID)PAGE_ROUND_DOWN(Address);
1896
1897 /*
1898 * Get the segment and section.
1899 */
1900 Context.Segment = MemoryArea->Data.SectionData.Segment;
1901 Context.Section = MemoryArea->Data.SectionData.Section;
1902 Context.SectionEntry = Entry;
1903 Context.CallingProcess = Process;
1904
1905 Context.Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
1906 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
1907
1908 DirectMapped = FALSE;
1909
1910 MmLockSectionSegment(Context.Segment);
1911
1912 #ifndef NEWCC
1913 FileOffset = Context.Offset.QuadPart + Context.Segment->Image.FileOffset;
1914 IsImageSection = Context.Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
1915 FileObject = Context.Section->FileObject;
1916
1917 if (FileObject != NULL &&
1918 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
1919 {
1920 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
1921
1922 /*
1923 * If the file system is letting us go directly to the cache and the
1924 * memory area was mapped at an offset in the file which is page aligned
1925 * then note this is a direct mapped page.
1926 */
1927 if ((FileOffset % PAGE_SIZE) == 0 &&
1928 (Context.Offset.QuadPart + PAGE_SIZE <= Context.Segment->RawLength.QuadPart || !IsImageSection))
1929 {
1930 DirectMapped = TRUE;
1931 }
1932 }
1933 #endif
1934
1935
1936 /*
1937 * This should never happen since mappings of physical memory are never
1938 * placed in the rmap lists.
1939 */
1940 if (Context.Section->AllocationAttributes & SEC_PHYSICALMEMORY)
1941 {
1942 DPRINT1("Trying to page out from physical memory section address 0x%p "
1943 "process %p\n", Address,
1944 Process ? Process->UniqueProcessId : 0);
1945 KeBugCheck(MEMORY_MANAGEMENT);
1946 }
1947
1948 /*
1949 * Get the section segment entry and the physical address.
1950 */
1951 if (!MmIsPagePresent(Process, Address))
1952 {
1953 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
1954 Process ? Process->UniqueProcessId : 0, Address);
1955 KeBugCheck(MEMORY_MANAGEMENT);
1956 }
1957 Page = MmGetPfnForProcess(Process, Address);
1958 SwapEntry = MmGetSavedSwapEntryPage(Page);
1959
1960 /*
1961 * Check the reference count to ensure this page can be paged out
1962 */
1963 if (MmGetReferenceCountPage(Page) != 1)
1964 {
1965 DPRINT("Cannot page out locked section page: 0x%lu (RefCount: %lu)\n",
1966 Page, MmGetReferenceCountPage(Page));
1967 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
1968 MmUnlockSectionSegment(Context.Segment);
1969 return STATUS_UNSUCCESSFUL;
1970 }
1971
1972 /*
1973 * Prepare the context structure for the rmap delete call.
1974 */
1975 MmUnlockSectionSegment(Context.Segment);
1976 Context.WasDirty = FALSE;
1977 if (Context.Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
1978 IS_SWAP_FROM_SSE(Entry) ||
1979 PFN_FROM_SSE(Entry) != Page)
1980 {
1981 Context.Private = TRUE;
1982 }
1983 else
1984 {
1985 Context.Private = FALSE;
1986 }
1987
1988 /*
1989 * Take an additional reference to the page or the VACB.
1990 */
1991 if (DirectMapped && !Context.Private)
1992 {
1993 if(!MiIsPageFromCache(MemoryArea, Context.Offset.QuadPart))
1994 {
1995 DPRINT1("Direct mapped non private page is not associated with the cache.\n");
1996 KeBugCheck(MEMORY_MANAGEMENT);
1997 }
1998 }
1999 else
2000 {
2001 OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
2002 MmReferencePage(Page);
2003 KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
2004 }
2005
2006 MmDeleteAllRmaps(Page, (PVOID)&Context, MmPageOutDeleteMapping);
2007
2008 /* Since we passed in a surrogate, we'll get back the page entry
2009 * state in our context. This is intended to make intermediate
2010 * decrements of share count not release the wait entry.
2011 */
2012 Entry = Context.SectionEntry;
2013
2014 /*
2015 * If this wasn't a private page then we should have reduced the entry to
2016 * zero by deleting all the rmaps.
2017 */
2018 if (!Context.Private && Entry != 0)
2019 {
2020 if (!(Context.Segment->Flags & MM_PAGEFILE_SEGMENT) &&
2021 !(Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2022 {
2023 KeBugCheckEx(MEMORY_MANAGEMENT, Entry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2024 }
2025 }
2026
2027 /*
2028 * If the page wasn't dirty then we can just free it as for a readonly page.
2029 * Since we unmapped all the mappings above we know it will not suddenly
2030 * become dirty.
2031 * If the page is from a pagefile section and has no swap entry,
2032 * we can't free the page at this point.
2033 */
2034 SwapEntry = MmGetSavedSwapEntryPage(Page);
2035 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT)
2036 {
2037 if (Context.Private)
2038 {
2039 DPRINT1("Found a %s private page (address %p) in a pagefile segment.\n",
2040 Context.WasDirty ? "dirty" : "clean", Address);
2041 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2042 }
2043 if (!Context.WasDirty && SwapEntry != 0)
2044 {
2045 MmSetSavedSwapEntryPage(Page, 0);
2046 MmLockSectionSegment(Context.Segment);
2047 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2048 MmUnlockSectionSegment(Context.Segment);
2049 MmReleasePageMemoryConsumer(MC_USER, Page);
2050 MiSetPageEvent(NULL, NULL);
2051 return(STATUS_SUCCESS);
2052 }
2053 }
2054 else if (Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2055 {
2056 if (Context.Private)
2057 {
2058 DPRINT1("Found a %s private page (address %p) in a shared section segment.\n",
2059 Context.WasDirty ? "dirty" : "clean", Address);
2060 KeBugCheckEx(MEMORY_MANAGEMENT, Page, (ULONG_PTR)Process, (ULONG_PTR)Address, 0);
2061 }
2062 if (!Context.WasDirty || SwapEntry != 0)
2063 {
2064 MmSetSavedSwapEntryPage(Page, 0);
2065 if (SwapEntry != 0)
2066 {
2067 MmLockSectionSegment(Context.Segment);
2068 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2069 MmUnlockSectionSegment(Context.Segment);
2070 }
2071 MmReleasePageMemoryConsumer(MC_USER, Page);
2072 MiSetPageEvent(NULL, NULL);
2073 return(STATUS_SUCCESS);
2074 }
2075 }
2076 else if (!Context.Private && DirectMapped)
2077 {
2078 if (SwapEntry != 0)
2079 {
2080 DPRINT1("Found a swapentry for a non private and direct mapped page (address %p)\n",
2081 Address);
2082 KeBugCheckEx(MEMORY_MANAGEMENT, STATUS_UNSUCCESSFUL, SwapEntry, (ULONG_PTR)Process, (ULONG_PTR)Address);
2083 }
2084 #ifndef NEWCC
2085 Status = CcRosUnmapVacb(SharedCacheMap, FileOffset, FALSE);
2086 #else
2087 Status = STATUS_SUCCESS;
2088 #endif
2089 #ifndef NEWCC
2090 if (!NT_SUCCESS(Status))
2091 {
2092 DPRINT1("CcRosUnmapVacb failed, status = %x\n", Status);
2093 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)SharedCacheMap, (ULONG_PTR)FileOffset, (ULONG_PTR)Address);
2094 }
2095 #endif
2096 MiSetPageEvent(NULL, NULL);
2097 return(STATUS_SUCCESS);
2098 }
2099 else if (!Context.WasDirty && !DirectMapped && !Context.Private)
2100 {
2101 if (SwapEntry != 0)
2102 {
2103 DPRINT1("Found a swap entry for a non dirty, non private and not direct mapped page (address %p)\n",
2104 Address);
2105 KeBugCheckEx(MEMORY_MANAGEMENT, SwapEntry, Page, (ULONG_PTR)Process, (ULONG_PTR)Address);
2106 }
2107 MmReleasePageMemoryConsumer(MC_USER, Page);
2108 MiSetPageEvent(NULL, NULL);
2109 return(STATUS_SUCCESS);
2110 }
2111 else if (!Context.WasDirty && Context.Private && SwapEntry != 0)
2112 {
2113 DPRINT("Not dirty and private and not swapped (%p:%p)\n", Process, Address);
2114 MmSetSavedSwapEntryPage(Page, 0);
2115 MmLockAddressSpace(AddressSpace);
2116 Status = MmCreatePageFileMapping(Process,
2117 Address,
2118 SwapEntry);
2119 MmUnlockAddressSpace(AddressSpace);
2120 if (!NT_SUCCESS(Status))
2121 {
2122 DPRINT1("Status %x Swapping out %p:%p\n", Status, Process, Address);
2123 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2124 }
2125 MmReleasePageMemoryConsumer(MC_USER, Page);
2126 MiSetPageEvent(NULL, NULL);
2127 return(STATUS_SUCCESS);
2128 }
2129
2130 /*
2131 * If necessary, allocate an entry in the paging file for this page
2132 */
2133 if (SwapEntry == 0)
2134 {
2135 SwapEntry = MmAllocSwapPage();
2136 if (SwapEntry == 0)
2137 {
2138 MmShowOutOfSpaceMessagePagingFile();
2139 MmLockAddressSpace(AddressSpace);
2140 /*
2141 * For private pages restore the old mappings.
2142 */
2143 if (Context.Private)
2144 {
2145 Status = MmCreateVirtualMapping(Process,
2146 Address,
2147 MemoryArea->Protect,
2148 &Page,
2149 1);
2150 MmSetDirtyPage(Process, Address);
2151 MmInsertRmap(Page,
2152 Process,
2153 Address);
2154 }
2155 else
2156 {
2157 ULONG_PTR OldEntry;
2158 /*
2159 * For non-private pages if the page wasn't direct mapped then
2160 * set it back into the section segment entry so we don't loose
2161 * our copy. Otherwise it will be handled by the cache manager.
2162 */
2163 Status = MmCreateVirtualMapping(Process,
2164 Address,
2165 MemoryArea->Protect,
2166 &Page,
2167 1);
2168 MmSetDirtyPage(Process, Address);
2169 MmInsertRmap(Page,
2170 Process,
2171 Address);
2172 // If we got here, the previous entry should have been a wait
2173 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2174 MmLockSectionSegment(Context.Segment);
2175 OldEntry = MmGetPageEntrySectionSegment(Context.Segment, &Context.Offset);
2176 ASSERT(OldEntry == 0 || OldEntry == MAKE_SWAP_SSE(MM_WAIT_ENTRY));
2177 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2178 MmUnlockSectionSegment(Context.Segment);
2179 }
2180 MmUnlockAddressSpace(AddressSpace);
2181 MiSetPageEvent(NULL, NULL);
2182 return(STATUS_PAGEFILE_QUOTA);
2183 }
2184 }
2185
2186 /*
2187 * Write the page to the pagefile
2188 */
2189 Status = MmWriteToSwapPage(SwapEntry, Page);
2190 if (!NT_SUCCESS(Status))
2191 {
2192 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2193 Status);
2194 /*
2195 * As above: undo our actions.
2196 * FIXME: Also free the swap page.
2197 */
2198 MmLockAddressSpace(AddressSpace);
2199 if (Context.Private)
2200 {
2201 Status = MmCreateVirtualMapping(Process,
2202 Address,
2203 MemoryArea->Protect,
2204 &Page,
2205 1);
2206 MmSetDirtyPage(Process, Address);
2207 MmInsertRmap(Page,
2208 Process,
2209 Address);
2210 }
2211 else
2212 {
2213 Status = MmCreateVirtualMapping(Process,
2214 Address,
2215 MemoryArea->Protect,
2216 &Page,
2217 1);
2218 MmSetDirtyPage(Process, Address);
2219 MmInsertRmap(Page,
2220 Process,
2221 Address);
2222 Entry = MAKE_SSE(Page << PAGE_SHIFT, 1);
2223 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2224 }
2225 MmUnlockAddressSpace(AddressSpace);
2226 MiSetPageEvent(NULL, NULL);
2227 return(STATUS_UNSUCCESSFUL);
2228 }
2229
2230 /*
2231 * Otherwise we have succeeded.
2232 */
2233 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2234 MmSetSavedSwapEntryPage(Page, 0);
2235 if (Context.Segment->Flags & MM_PAGEFILE_SEGMENT ||
2236 Context.Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2237 {
2238 MmLockSectionSegment(Context.Segment);
2239 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, MAKE_SWAP_SSE(SwapEntry));
2240 MmUnlockSectionSegment(Context.Segment);
2241 }
2242 else
2243 {
2244 MmReleasePageMemoryConsumer(MC_USER, Page);
2245 }
2246
2247 if (Context.Private)
2248 {
2249 MmLockAddressSpace(AddressSpace);
2250 MmLockSectionSegment(Context.Segment);
2251 Status = MmCreatePageFileMapping(Process,
2252 Address,
2253 SwapEntry);
2254 /* We had placed a wait entry upon entry ... replace it before leaving */
2255 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2256 MmUnlockSectionSegment(Context.Segment);
2257 MmUnlockAddressSpace(AddressSpace);
2258 if (!NT_SUCCESS(Status))
2259 {
2260 DPRINT1("Status %x Creating page file mapping for %p:%p\n", Status, Process, Address);
2261 KeBugCheckEx(MEMORY_MANAGEMENT, Status, (ULONG_PTR)Process, (ULONG_PTR)Address, SwapEntry);
2262 }
2263 }
2264 else
2265 {
2266 MmLockAddressSpace(AddressSpace);
2267 MmLockSectionSegment(Context.Segment);
2268 Entry = MAKE_SWAP_SSE(SwapEntry);
2269 /* We had placed a wait entry upon entry ... replace it before leaving */
2270 MmSetPageEntrySectionSegment(Context.Segment, &Context.Offset, Entry);
2271 MmUnlockSectionSegment(Context.Segment);
2272 MmUnlockAddressSpace(AddressSpace);
2273 }
2274
2275 MiSetPageEvent(NULL, NULL);
2276 return(STATUS_SUCCESS);
2277 }
2278
2279 NTSTATUS
2280 NTAPI
2281 MmWritePageSectionView(PMMSUPPORT AddressSpace,
2282 PMEMORY_AREA MemoryArea,
2283 PVOID Address,
2284 ULONG PageEntry)
2285 {
2286 LARGE_INTEGER Offset;
2287 PROS_SECTION_OBJECT Section;
2288 PMM_SECTION_SEGMENT Segment;
2289 PFN_NUMBER Page;
2290 SWAPENTRY SwapEntry;
2291 ULONG_PTR Entry;
2292 BOOLEAN Private;
2293 NTSTATUS Status;
2294 PFILE_OBJECT FileObject;
2295 #ifndef NEWCC
2296 PROS_SHARED_CACHE_MAP SharedCacheMap = NULL;
2297 #endif
2298 BOOLEAN DirectMapped;
2299 BOOLEAN IsImageSection;
2300 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2301
2302 Address = (PVOID)PAGE_ROUND_DOWN(Address);
2303
2304 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
2305 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2306
2307 /*
2308 * Get the segment and section.
2309 */
2310 Segment = MemoryArea->Data.SectionData.Segment;
2311 Section = MemoryArea->Data.SectionData.Section;
2312 IsImageSection = Section->AllocationAttributes & SEC_IMAGE ? TRUE : FALSE;
2313
2314 FileObject = Section->FileObject;
2315 DirectMapped = FALSE;
2316 if (FileObject != NULL &&
2317 !(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED))
2318 {
2319 #ifndef NEWCC
2320 SharedCacheMap = FileObject->SectionObjectPointer->SharedCacheMap;
2321 #endif
2322
2323 /*
2324 * If the file system is letting us go directly to the cache and the
2325 * memory area was mapped at an offset in the file which is page aligned
2326 * then note this is a direct mapped page.
2327 */
2328 if (((Offset.QuadPart + Segment->Image.FileOffset) % PAGE_SIZE) == 0 &&
2329 (Offset.QuadPart + PAGE_SIZE <= Segment->RawLength.QuadPart || !IsImageSection))
2330 {
2331 DirectMapped = TRUE;
2332 }
2333 }
2334
2335 /*
2336 * This should never happen since mappings of physical memory are never
2337 * placed in the rmap lists.
2338 */
2339 if (Section->AllocationAttributes & SEC_PHYSICALMEMORY)
2340 {
2341 DPRINT1("Trying to write back page from physical memory mapped at %p "
2342 "process %p\n", Address,
2343 Process ? Process->UniqueProcessId : 0);
2344 KeBugCheck(MEMORY_MANAGEMENT);
2345 }
2346
2347 /*
2348 * Get the section segment entry and the physical address.
2349 */
2350 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2351 if (!MmIsPagePresent(Process, Address))
2352 {
2353 DPRINT1("Trying to page out not-present page at (%p,0x%p).\n",
2354 Process ? Process->UniqueProcessId : 0, Address);
2355 KeBugCheck(MEMORY_MANAGEMENT);
2356 }
2357 Page = MmGetPfnForProcess(Process, Address);
2358 SwapEntry = MmGetSavedSwapEntryPage(Page);
2359
2360 /*
2361 * Check for a private (COWed) page.
2362 */
2363 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2364 IS_SWAP_FROM_SSE(Entry) ||
2365 PFN_FROM_SSE(Entry) != Page)
2366 {
2367 Private = TRUE;
2368 }
2369 else
2370 {
2371 Private = FALSE;
2372 }
2373
2374 /*
2375 * Speculatively set all mappings of the page to clean.
2376 */
2377 MmSetCleanAllRmaps(Page);
2378
2379 /*
2380 * If this page was direct mapped from the cache then the cache manager
2381 * will take care of writing it back to disk.
2382 */
2383 if (DirectMapped && !Private)
2384 {
2385 //LARGE_INTEGER SOffset;
2386 ASSERT(SwapEntry == 0);
2387 //SOffset.QuadPart = Offset.QuadPart + Segment->Image.FileOffset;
2388 #ifndef NEWCC
2389 CcRosMarkDirtyVacb(SharedCacheMap, Offset.QuadPart);
2390 #endif
2391 MmLockSectionSegment(Segment);
2392 MmSetPageEntrySectionSegment(Segment, &Offset, PageEntry);
2393 MmUnlockSectionSegment(Segment);
2394 MiSetPageEvent(NULL, NULL);
2395 return(STATUS_SUCCESS);
2396 }
2397
2398 /*
2399 * If necessary, allocate an entry in the paging file for this page
2400 */
2401 if (SwapEntry == 0)
2402 {
2403 SwapEntry = MmAllocSwapPage();
2404 if (SwapEntry == 0)
2405 {
2406 MmSetDirtyAllRmaps(Page);
2407 MiSetPageEvent(NULL, NULL);
2408 return(STATUS_PAGEFILE_QUOTA);
2409 }
2410 MmSetSavedSwapEntryPage(Page, SwapEntry);
2411 }
2412
2413 /*
2414 * Write the page to the pagefile
2415 */
2416 Status = MmWriteToSwapPage(SwapEntry, Page);
2417 if (!NT_SUCCESS(Status))
2418 {
2419 DPRINT1("MM: Failed to write to swap page (Status was 0x%.8X)\n",
2420 Status);
2421 MmSetDirtyAllRmaps(Page);
2422 MiSetPageEvent(NULL, NULL);
2423 return(STATUS_UNSUCCESSFUL);
2424 }
2425
2426 /*
2427 * Otherwise we have succeeded.
2428 */
2429 DPRINT("MM: Wrote section page 0x%.8X to swap!\n", Page << PAGE_SHIFT);
2430 MiSetPageEvent(NULL, NULL);
2431 return(STATUS_SUCCESS);
2432 }
2433
2434 static VOID
2435 MmAlterViewAttributes(PMMSUPPORT AddressSpace,
2436 PVOID BaseAddress,
2437 SIZE_T RegionSize,
2438 ULONG OldType,
2439 ULONG OldProtect,
2440 ULONG NewType,
2441 ULONG NewProtect)
2442 {
2443 PMEMORY_AREA MemoryArea;
2444 PMM_SECTION_SEGMENT Segment;
2445 BOOLEAN DoCOW = FALSE;
2446 ULONG i;
2447 PEPROCESS Process = MmGetAddressSpaceOwner(AddressSpace);
2448
2449 MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
2450 ASSERT(MemoryArea != NULL);
2451 Segment = MemoryArea->Data.SectionData.Segment;
2452 MmLockSectionSegment(Segment);
2453
2454 if ((Segment->WriteCopy) &&
2455 (NewProtect == PAGE_READWRITE || NewProtect == PAGE_EXECUTE_READWRITE))
2456 {
2457 DoCOW = TRUE;
2458 }
2459
2460 if (OldProtect != NewProtect)
2461 {
2462 for (i = 0; i < PAGE_ROUND_UP(RegionSize) / PAGE_SIZE; i++)
2463 {
2464 SWAPENTRY SwapEntry;
2465 PVOID Address = (char*)BaseAddress + (i * PAGE_SIZE);
2466 ULONG Protect = NewProtect;
2467
2468 /* Wait for a wait entry to disappear */
2469 do
2470 {
2471 MmGetPageFileMapping(Process, Address, &SwapEntry);
2472 if (SwapEntry != MM_WAIT_ENTRY)
2473 break;
2474 MiWaitForPageEvent(Process, Address);
2475 }
2476 while (TRUE);
2477
2478 /*
2479 * If we doing COW for this segment then check if the page is
2480 * already private.
2481 */
2482 if (DoCOW && MmIsPagePresent(Process, Address))
2483 {
2484 LARGE_INTEGER Offset;
2485 ULONG_PTR Entry;
2486 PFN_NUMBER Page;
2487
2488 Offset.QuadPart = (ULONG_PTR)Address - MA_GetStartingAddress(MemoryArea)
2489 + MemoryArea->Data.SectionData.ViewOffset.QuadPart;
2490 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2491 /*
2492 * An MM_WAIT_ENTRY is ok in this case... It'll just count as
2493 * IS_SWAP_FROM_SSE and we'll do the right thing.
2494 */
2495 Page = MmGetPfnForProcess(Process, Address);
2496
2497 Protect = PAGE_READONLY;
2498 if (Segment->Image.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ||
2499 IS_SWAP_FROM_SSE(Entry) ||
2500 PFN_FROM_SSE(Entry) != Page)
2501 {
2502 Protect = NewProtect;
2503 }
2504 }
2505
2506 if (MmIsPagePresent(Process, Address) || MmIsDisabledPage(Process, Address))
2507 {
2508 MmSetPageProtect(Process, Address,
2509 Protect);
2510 }
2511 }
2512 }
2513
2514 MmUnlockSectionSegment(Segment);
2515 }
2516
2517 NTSTATUS
2518 NTAPI
2519 MmProtectSectionView(PMMSUPPORT AddressSpace,
2520 PMEMORY_AREA MemoryArea,
2521 PVOID BaseAddress,
2522 SIZE_T Length,
2523 ULONG Protect,
2524 PULONG OldProtect)
2525 {
2526 PMM_REGION Region;
2527 NTSTATUS Status;
2528 ULONG_PTR MaxLength;
2529
2530 MaxLength = MA_GetEndingAddress(MemoryArea) - (ULONG_PTR)BaseAddress;
2531 if (Length > MaxLength)
2532 Length = (ULONG)MaxLength;
2533
2534 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2535 &MemoryArea->Data.SectionData.RegionListHead,
2536 BaseAddress, NULL);
2537 ASSERT(Region != NULL);
2538
2539 if ((MemoryArea->Flags & SEC_NO_CHANGE) &&
2540 Region->Protect != Protect)
2541 {
2542 return STATUS_INVALID_PAGE_PROTECTION;
2543 }
2544
2545 *OldProtect = Region->Protect;
2546 Status = MmAlterRegion(AddressSpace, (PVOID)MA_GetStartingAddress(MemoryArea),
2547 &MemoryArea->Data.SectionData.RegionListHead,
2548 BaseAddress, Length, Region->Type, Protect,
2549 MmAlterViewAttributes);
2550
2551 return(Status);
2552 }
2553
2554 NTSTATUS NTAPI
2555 MmQuerySectionView(PMEMORY_AREA MemoryArea,
2556 PVOID Address,
2557 PMEMORY_BASIC_INFORMATION Info,
2558 PSIZE_T ResultLength)
2559 {
2560 PMM_REGION Region;
2561 PVOID RegionBaseAddress;
2562 PROS_SECTION_OBJECT Section;
2563 PMM_SECTION_SEGMENT Segment;
2564
2565 Region = MmFindRegion((PVOID)MA_GetStartingAddress(MemoryArea),
2566 &MemoryArea->Data.SectionData.RegionListHead,
2567 Address, &RegionBaseAddress);
2568 if (Region == NULL)
2569 {
2570 return STATUS_UNSUCCESSFUL;
2571 }
2572
2573 Section = MemoryArea->Data.SectionData.Section;
2574 if (Section->AllocationAttributes & SEC_IMAGE)
2575 {
2576 Segment = MemoryArea->Data.SectionData.Segment;
2577 Info->AllocationBase = (PUCHAR)MA_GetStartingAddress(MemoryArea) - Segment->Image.VirtualAddress;
2578 Info->Type = MEM_IMAGE;
2579 }
2580 else
2581 {
2582 Info->AllocationBase = (PVOID)MA_GetStartingAddress(MemoryArea);
2583 Info->Type = MEM_MAPPED;
2584 }
2585 Info->BaseAddress = RegionBaseAddress;
2586 Info->AllocationProtect = MemoryArea->Protect;
2587 Info->RegionSize = Region->Length;
2588 Info->State = MEM_COMMIT;
2589 Info->Protect = Region->Protect;
2590
2591 *ResultLength = sizeof(MEMORY_BASIC_INFORMATION);
2592 return(STATUS_SUCCESS);
2593 }
2594
2595 VOID
2596 NTAPI
2597 MmpFreePageFileSegment(PMM_SECTION_SEGMENT Segment)
2598 {
2599 ULONG Length;
2600 LARGE_INTEGER Offset;
2601 ULONG_PTR Entry;
2602 SWAPENTRY SavedSwapEntry;
2603 PFN_NUMBER Page;
2604
2605 Page = 0;
2606
2607 MmLockSectionSegment(Segment);
2608
2609 Length = PAGE_ROUND_UP(Segment->Length.QuadPart);
2610 for (Offset.QuadPart = 0; Offset.QuadPart < Length; Offset.QuadPart += PAGE_SIZE)
2611 {
2612 Entry = MmGetPageEntrySectionSegment(Segment, &Offset);
2613 if (Entry)
2614 {
2615 MmSetPageEntrySectionSegment(Segment, &Offset, 0);
2616 if (IS_SWAP_FROM_SSE(Entry))
2617 {
2618 MmFreeSwapPage(SWAPENTRY_FROM_SSE(Entry));
2619 }
2620 else
2621 {
2622 Page = PFN_FROM_SSE(Entry);
2623 SavedSwapEntry = MmGetSavedSwapEntryPage(Page);
2624 if (SavedSwapEntry != 0)
2625 {
2626 MmSetSavedSwapEntryPage(Page, 0);
2627 MmFreeSwapPage(SavedSwapEntry);
2628 }
2629 MmReleasePageMemoryConsumer(MC_USER, Page);
2630 }
2631 }
2632 }
2633
2634 MmUnlockSectionSegment(Segment);
2635 }
2636
2637 VOID NTAPI
2638 MmpDeleteSection(PVOID ObjectBody)
2639 {
2640 PROS_SECTION_OBJECT Section = (PROS_SECTION_OBJECT)ObjectBody;
2641
2642 /* Check if it's an ARM3, or ReactOS section */
2643 if (!MiIsRosSectionObject(Section))
2644 {
2645 MiDeleteARM3Section(ObjectBody);
2646 return;
2647 }
2648
2649 DPRINT("MmpDeleteSection(ObjectBody %p)\n", ObjectBody);
2650 if (Section->AllocationAttributes & SEC_IMAGE)
2651 {
2652 ULONG i;
2653 ULONG NrSegments;
2654 ULONG RefCount;
2655 PMM_SECTION_SEGMENT SectionSegments;
2656
2657 /*
2658 * NOTE: Section->ImageSection can be NULL for short time
2659 * during the section creating. If we fail for some reason
2660 * until the image section is properly initialized we shouldn't
2661 * process further here.
2662 */
2663 if (Section->ImageSection == NULL)
2664 return;
2665
2666 SectionSegments = Section->ImageSection->Segments;
2667 NrSegments = Section->ImageSection->NrSegments;
2668
2669 for (i = 0; i < NrSegments; i++)
2670 {
2671 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2672 {
2673 MmLockSectionSegment(&SectionSegments[i]);
2674 }
2675 RefCount = InterlockedDecrementUL(&SectionSegments[i].ReferenceCount);
2676 if (SectionSegments[i].Image.Characteristics & IMAGE_SCN_MEM_SHARED)
2677 {
2678 MmUnlockSectionSegment(&SectionSegments[i]);
2679 if (RefCount == 0)
2680 {
2681 MmpFreePageFileSegment(&SectionSegments[i]);
2682 }
2683 }
2684 }
2685 }
2686 #ifdef NEWCC
2687 else if (Section->Segment && Section->Segment->Flags & MM_DATAFILE_SEGMENT)
2688 {
2689 ULONG RefCount = 0;
2690 PMM_SECTION_SEGMENT Segment = Section->Segment;
2691
2692 if (Segment &&
2693 (RefCount = InterlockedDecrementUL(&Segment->ReferenceCount)) == 0)
2694 {
2695 DPRINT("Freeing section segment\n");
2696 Section->Segment = NULL;
2697 MmFinalizeSegment(Segment);
2698 }
2699 else
2700 {
2701 DPRINT("RefCount %d\n", RefCount);
2702 }
2703 }
2704 #endif
2705 else
2706 {
2707 /*
2708 * NOTE: Section->Segment can be NULL for short time
2709 * during the section creating.
2710 */
2711 if (Section->Segment == NULL)
2712 return;
2713
2714 if (Section->Segment->Flags & MM_PAGEFILE_SEGMENT)
2715 {
2716 MmpFreePageFileSegment(Section->Segment);
2717 MmFreePageTablesSectionSegment(Section->Segment, NULL);
2718 ExFreePool(Section->Segment);
2719 Section->Segment = NULL;
2720 }
2721 else
2722 {
2723 (void)InterlockedDecrementUL(&Section->Segment->ReferenceCount);
2724 }
2725 }
2726 if (Section->FileObject != NULL)
2727 {
2728 #ifndef NEWCC
2729 CcRosDereferenceCache(Section->FileObject);
2730 #endif
2731 ObDereferenceObject(Section->FileObject);
2732 Section->FileObject = NULL;
2733 }
2734 }
2735
2736 VOID NTAPI
2737 MmpCloseSection(IN PEPROCESS Process OPTIONAL,
2738 IN PVOID Object,
2739 IN ACCESS_MASK GrantedAccess,
2740 IN ULONG ProcessHandleCount,
2741 IN ULONG SystemHandleCount)
2742 {
2743 DPRINT("MmpCloseSection(OB %p, HC %lu)\n", Object, ProcessHandleCount);
2744 }
2745
2746 NTSTATUS
2747 INIT_FUNCTION
2748 NTAPI
2749 MmCreatePhysicalMemorySection(VOID)
2750 {
2751 PROS_SECTION_OBJECT PhysSection;
2752 NTSTATUS Status;
2753 OBJECT_ATTRIBUTES Obj;
2754 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\Device\\PhysicalMemory");
2755 LARGE_INTEGER SectionSize;
2756 HANDLE Handle;
2757
2758 /*
2759 * Create the section mapping physical memory
2760 */
2761 SectionSize.QuadPart = 0xFFFFFFFF;
2762 InitializeObjectAttributes(&Obj,
2763 &Name,
2764 OBJ_PERMANENT | OBJ_KERNEL_EXCLUSIVE,
2765 NULL,
2766 NULL);
2767 Status = MmCreateSection((PVOID)&PhysSection,
2768 SECTION_ALL_ACCESS,
2769 &Obj,
2770 &SectionSize,
2771 PAGE_EXECUTE_READWRITE,
2772 SEC_PHYSICALMEMORY,
2773 NULL,
2774 NULL);
2775 if (!NT_SUCCESS(Status))
2776 {
2777 DPRINT1("Failed to create PhysicalMemory section\n");
2778 KeBugCheck(MEMORY_MANAGEMENT);
2779 }
2780 Status = ObInsertObject(PhysSection,
2781 NULL,
2782 SECTION_ALL_ACCESS,
2783 0,
2784 NULL,
2785 &Handle);
2786 if (!NT_SUCCESS(Status))
2787 {
2788 ObDereferenceObject(PhysSection);
2789 }
2790 ObCloseHandle(Handle, KernelMode);
2791 PhysSection->AllocationAttributes |= SEC_PHYSICALMEMORY;
2792 PhysSection->Segment->Flags &= ~MM_PAGEFILE_SEGMENT;
2793
2794 return(STATUS_SUCCESS);
2795 }
2796
2797 NTSTATUS
2798 INIT_FUNCTION
2799 NTAPI
2800 MmInitSectionImplementation(VOID)
2801 {
2802 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
2803 UNICODE_STRING Name;
2804
2805 DPRINT("Creating Section Object Type\n");
2806
2807 /* Initialize the section based root */
2808 ASSERT(MmSectionBasedRoot.NumberGenericTableElements == 0);
2809 MmSectionBasedRoot.BalancedRoot.u1.Parent = &MmSectionBasedRoot.BalancedRoot;
2810
2811 /* Initialize the Section object type */
2812 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
2813 RtlInitUnicodeString(&Name, L"Section");
2814 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
2815 ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(ROS_SECTION_OBJECT);
2816 ObjectTypeInitializer.PoolType = PagedPool;
2817 ObjectTypeInitializer.UseDefaultObject = TRUE;
2818 ObjectTypeInitializer.GenericMapping = MmpSectionMapping;
2819 ObjectTypeInitializer.DeleteProcedure = MmpDeleteSection;
2820 ObjectTypeInitializer.CloseProcedure = MmpCloseSection;
2821 ObjectTypeInitializer.ValidAccessMask = SECTION_ALL_ACCESS;
2822 ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
2823 ObCreateObjectType(&Name, &ObjectTypeInitializer, NULL, &MmSectionObjectType);
2824
2825 MmCreatePhysicalMemorySection();
2826
2827 return(STATUS_SUCCESS);
2828 }
2829
2830 NTSTATUS
2831 NTAPI
2832 MmCreatePageFileSection(PROS_SECTION_OBJECT *SectionObject,
2833 ACCESS_MASK DesiredAccess,
2834 POBJECT_ATTRIBUTES ObjectAttributes,
2835 PLARGE_INTEGER UMaximumSize,
2836 ULONG SectionPageProtection,
2837 ULONG AllocationAttributes)
2838 /*
2839 * Create a section which is backed by the pagefile
2840 */
2841 {
2842 LARGE_INTEGER MaximumSize;
2843 PROS_SECTION_OBJECT Section;
2844 PMM_SECTION_SEGMENT Segment;
2845 NTSTATUS Status;
2846
2847 if (UMaximumSize == NULL)
2848 {
2849 DPRINT1("MmCreatePageFileSection: (UMaximumSize == NULL)\n");
2850 return(STATUS_INVALID_PARAMETER);
2851 }
2852 MaximumSize = *UMaximumSize;
2853
2854 /*
2855 * Create the section
2856 */
2857 Status = ObCreateObject(ExGetPreviousMode(),
2858 MmSectionObjectType,
2859 ObjectAttributes,
2860 ExGetPreviousMode(),
2861 NULL,
2862 sizeof(ROS_SECTION_OBJECT),
2863 0,
2864 0,
2865 (PVOID*)(PVOID)&Section);
2866 if (!NT_SUCCESS(Status))
2867 {
2868 DPRINT1("MmCreatePageFileSection: failed to create object (0x%lx)\n", Status);
2869 return(Status);
2870 }
2871
2872 /*
2873 * Initialize it
2874 */
2875 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2876 Section->Type = 'SC';
2877 Section->Size = 'TN';
2878 Section->SectionPageProtection = SectionPageProtection;
2879 Section->AllocationAttributes = AllocationAttributes;
2880 Section->MaximumSize = MaximumSize;
2881 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
2882 TAG_MM_SECTION_SEGMENT);
2883 if (Segment == NULL)
2884 {
2885 ObDereferenceObject(Section);
2886 return(STATUS_NO_MEMORY);
2887 }
2888 RtlZeroMemory(Segment, sizeof(MM_SECTION_SEGMENT));
2889 Section->Segment = Segment;
2890 Segment->ReferenceCount = 1;
2891 ExInitializeFastMutex(&Segment->Lock);
2892 Segment->Image.FileOffset = 0;
2893 Segment->Protection = SectionPageProtection;
2894 Segment->RawLength.QuadPart = MaximumSize.u.LowPart;
2895 Segment->Length.QuadPart = PAGE_ROUND_UP(MaximumSize.u.LowPart);
2896 Segment->Flags = MM_PAGEFILE_SEGMENT;
2897 Segment->WriteCopy = FALSE;
2898 Segment->Image.VirtualAddress = 0;
2899 Segment->Image.Characteristics = 0;
2900 *SectionObject = Section;
2901 MiInitializeSectionPageTable(Segment);
2902 return(STATUS_SUCCESS);
2903 }
2904
2905 NTSTATUS
2906 NTAPI
2907 MmCreateDataFileSection(PROS_SECTION_OBJECT *SectionObject,
2908 ACCESS_MASK DesiredAccess,
2909 POBJECT_ATTRIBUTES ObjectAttributes,
2910 PLARGE_INTEGER UMaximumSize,
2911 ULONG SectionPageProtection,
2912 ULONG AllocationAttributes,
2913 HANDLE FileHandle)
2914 /*
2915 * Create a section backed by a data file
2916 */
2917 {
2918 PROS_SECTION_OBJECT Section;
2919 NTSTATUS Status;
2920 LARGE_INTEGER MaximumSize;
2921 PFILE_OBJECT FileObject;
2922 PMM_SECTION_SEGMENT Segment;
2923 ULONG FileAccess;
2924 IO_STATUS_BLOCK Iosb;
2925 LARGE_INTEGER Offset;
2926 CHAR Buffer;
2927 FILE_STANDARD_INFORMATION FileInfo;
2928 ULONG Length;
2929
2930 /*
2931 * Create the section
2932 */
2933 Status = ObCreateObject(ExGetPreviousMode(),
2934 MmSectionObjectType,
2935 ObjectAttributes,
2936 ExGetPreviousMode(),
2937 NULL,
2938 sizeof(ROS_SECTION_OBJECT),
2939 0,
2940 0,
2941 (PVOID*)&Section);
2942 if (!NT_SUCCESS(Status))
2943 {
2944 return(Status);
2945 }
2946 /*
2947 * Initialize it
2948 */
2949 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
2950 Section->Type = 'SC';
2951 Section->Size = 'TN';
2952 Section->SectionPageProtection = SectionPageProtection;
2953 Section->AllocationAttributes = AllocationAttributes;
2954
2955 /*
2956 * Reference the file handle
2957 */
2958 FileAccess = MiArm3GetCorrectFileAccessMask(SectionPageProtection);
2959 Status = ObReferenceObjectByHandle(FileHandle,
2960 FileAccess,
2961 IoFileObjectType,
2962 ExGetPreviousMode(),
2963 (PVOID*)(PVOID)&FileObject,
2964 NULL);
2965 if (!NT_SUCCESS(Status))
2966 {
2967 ObDereferenceObject(Section);
2968 return(Status);
2969 }
2970
2971 /*
2972 * FIXME: This is propably not entirely correct. We can't look into
2973 * the standard FCB header because it might not be initialized yet
2974 * (as in case of the EXT2FS driver by Manoj Paul Joseph where the
2975 * standard file information is filled on first request).
2976 */
2977 Status = IoQueryFileInformation(FileObject,
2978 FileStandardInformation,
2979 sizeof(FILE_STANDARD_INFORMATION),
2980 &FileInfo,
2981 &Length);
2982 Iosb.Information = Length;
2983 if (!NT_SUCCESS(Status))
2984 {
2985 ObDereferenceObject(Section);
2986 ObDereferenceObject(FileObject);
2987 return Status;
2988 }
2989
2990 /*
2991 * FIXME: Revise this once a locking order for file size changes is
2992 * decided
2993 */
2994 if ((UMaximumSize != NULL) && (UMaximumSize->QuadPart != 0))
2995 {
2996 MaximumSize = *UMaximumSize;
2997 }
2998 else
2999 {
3000 MaximumSize = FileInfo.EndOfFile;
3001 /* Mapping zero-sized files isn't allowed. */
3002 if (MaximumSize.QuadPart == 0)
3003 {
3004 ObDereferenceObject(Section);
3005 ObDereferenceObject(FileObject);
3006 return STATUS_MAPPED_FILE_SIZE_ZERO;
3007 }
3008 }
3009
3010 if (MaximumSize.QuadPart > FileInfo.EndOfFile.QuadPart)
3011 {
3012 Status = IoSetInformation(FileObject,
3013 FileAllocationInformation,
3014 sizeof(LARGE_INTEGER),
3015 &MaximumSize);
3016 if (!NT_SUCCESS(Status))
3017 {
3018 ObDereferenceObject(Section);
3019 ObDereferenceObject(FileObject);
3020 return(STATUS_SECTION_NOT_EXTENDED);
3021 }
3022 }
3023
3024 if (FileObject->SectionObjectPointer == NULL ||
3025 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3026 {
3027 /*
3028 * Read a bit so caching is initiated for the file object.
3029 * This is only needed because MiReadPage currently cannot
3030 * handle non-cached streams.
3031 */
3032 Offset.QuadPart = 0;
3033 Status = ZwReadFile(FileHandle,
3034 NULL,
3035 NULL,
3036 NULL,
3037 &Iosb,
3038 &Buffer,
3039 sizeof (Buffer),
3040 &Offset,
3041 0);
3042 if (!NT_SUCCESS(Status) && (Status != STATUS_END_OF_FILE))
3043 {
3044 ObDereferenceObject(Section);
3045 ObDereferenceObject(FileObject);
3046 return(Status);
3047 }
3048 if (FileObject->SectionObjectPointer == NULL ||
3049 FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3050 {
3051 /* FIXME: handle this situation */
3052 ObDereferenceObject(Section);
3053 ObDereferenceObject(FileObject);
3054 return STATUS_INVALID_PARAMETER;
3055 }
3056 }
3057
3058 /*
3059 * Lock the file
3060 */
3061 Status = MmspWaitForFileLock(FileObject);
3062 if (Status != STATUS_SUCCESS)
3063 {
3064 ObDereferenceObject(Section);
3065 ObDereferenceObject(FileObject);
3066 return(Status);
3067 }
3068
3069 /*
3070 * If this file hasn't been mapped as a data file before then allocate a
3071 * section segment to describe the data file mapping
3072 */
3073 if (FileObject->SectionObjectPointer->DataSectionObject == NULL)
3074 {
3075 Segment = ExAllocatePoolWithTag(NonPagedPool, sizeof(MM_SECTION_SEGMENT),
3076 TAG_MM_SECTION_SEGMENT);
3077 if (Segment == NULL)
3078 {
3079 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3080 ObDereferenceObject(Section);
3081 ObDereferenceObject(FileObject);
3082 return(STATUS_NO_MEMORY);
3083 }
3084 Section->Segment = Segment;
3085 Segment->ReferenceCount = 1;
3086 ExInitializeFastMutex(&Segment->Lock);
3087 /*
3088 * Set the lock before assigning the segment to the file object
3089 */
3090 ExAcquireFastMutex(&Segment->Lock);
3091 FileObject->SectionObjectPointer->DataSectionObject = (PVOID)Segment;
3092
3093 Segment->Image.FileOffset = 0;
3094 Segment->Protection = SectionPageProtection;
3095 Segment->Flags = MM_DATAFILE_SEGMENT;
3096 Segment->Image.Characteristics = 0;
3097 Segment->WriteCopy = (SectionPageProtection & (PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY));
3098 if (AllocationAttributes & SEC_RESERVE)
3099 {
3100 Segment->Length.QuadPart = Segment->RawLength.QuadPart = 0;
3101 }
3102 else
3103 {
3104 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3105 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3106 }
3107 Segment->Image.VirtualAddress = 0;
3108 Segment->Locked = TRUE;
3109 MiInitializeSectionPageTable(Segment);
3110 }
3111 else
3112 {
3113 /*
3114 * If the file is already mapped as a data file then we may need
3115 * to extend it
3116 */
3117 Segment =
3118 (PMM_SECTION_SEGMENT)FileObject->SectionObjectPointer->
3119 DataSectionObject;
3120 Section->Segment = Segment;
3121 (void)InterlockedIncrementUL(&Segment->ReferenceCount);
3122 MmLockSectionSegment(Segment);
3123
3124 if (MaximumSize.QuadPart > Segment->RawLength.QuadPart &&
3125 !(AllocationAttributes & SEC_RESERVE))
3126 {
3127 Segment->RawLength.QuadPart = MaximumSize.QuadPart;
3128 Segment->Length.QuadPart = PAGE_ROUND_UP(Segment->RawLength.QuadPart);
3129 }
3130 }
3131 MmUnlockSectionSegment(Segment);
3132 Section->FileObject = FileObject;
3133 Section->MaximumSize = MaximumSize;
3134 #ifndef NEWCC
3135 CcRosReferenceCache(FileObject);
3136 #endif
3137 //KeSetEvent((PVOID)&FileObject->Lock, IO_NO_INCREMENT, FALSE);
3138 *SectionObject = Section;
3139 return(STATUS_SUCCESS);
3140 }
3141
3142 /*
3143 TODO: not that great (declaring loaders statically, having to declare all of
3144 them, having to keep them extern, etc.), will fix in the future
3145 */
3146 extern NTSTATUS NTAPI PeFmtCreateSection
3147 (
3148 IN CONST VOID * FileHeader,
3149 IN SIZE_T FileHeaderSize,
3150 IN PVOID File,
3151 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3152 OUT PULONG Flags,
3153 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3154 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3155 );
3156
3157 extern NTSTATUS NTAPI ElfFmtCreateSection
3158 (
3159 IN CONST VOID * FileHeader,
3160 IN SIZE_T FileHeaderSize,
3161 IN PVOID File,
3162 OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3163 OUT PULONG Flags,
3164 IN PEXEFMT_CB_READ_FILE ReadFileCb,
3165 IN PEXEFMT_CB_ALLOCATE_SEGMENTS AllocateSegmentsCb
3166 );
3167
3168 /* TODO: this is a standard DDK/PSDK macro */
3169 #ifndef RTL_NUMBER_OF
3170 #define RTL_NUMBER_OF(ARR_) (sizeof(ARR_) / sizeof((ARR_)[0]))
3171 #endif
3172
3173 static PEXEFMT_LOADER ExeFmtpLoaders[] =
3174 {
3175 PeFmtCreateSection,
3176 #ifdef __ELF
3177 ElfFmtCreateSection
3178 #endif
3179 };
3180
3181 static
3182 PMM_SECTION_SEGMENT
3183 NTAPI
3184 ExeFmtpAllocateSegments(IN ULONG NrSegments)
3185 {
3186 SIZE_T SizeOfSegments;
3187 PMM_SECTION_SEGMENT Segments;
3188
3189 /* TODO: check for integer overflow */
3190 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * NrSegments;
3191
3192 Segments = ExAllocatePoolWithTag(NonPagedPool,
3193 SizeOfSegments,
3194 TAG_MM_SECTION_SEGMENT);
3195
3196 if(Segments)
3197 RtlZeroMemory(Segments, SizeOfSegments);
3198
3199 return Segments;
3200 }
3201
3202 static
3203 NTSTATUS
3204 NTAPI
3205 ExeFmtpReadFile(IN PVOID File,
3206 IN PLARGE_INTEGER Offset,
3207 IN ULONG Length,
3208 OUT PVOID * Data,
3209 OUT PVOID * AllocBase,
3210 OUT PULONG ReadSize)
3211 {
3212 NTSTATUS Status;
3213 LARGE_INTEGER FileOffset;
3214 ULONG AdjustOffset;
3215 ULONG OffsetAdjustment;
3216 ULONG BufferSize;
3217 ULONG UsedSize;
3218 PVOID Buffer;
3219 PFILE_OBJECT FileObject = File;
3220 IO_STATUS_BLOCK Iosb;
3221
3222 ASSERT_IRQL_LESS(DISPATCH_LEVEL);
3223
3224 if(Length == 0)
3225 {
3226 KeBugCheck(MEMORY_MANAGEMENT);
3227 }
3228
3229 FileOffset = *Offset;
3230
3231 /* Negative/special offset: it cannot be used in this context */
3232 if(FileOffset.u.HighPart < 0)
3233 {
3234 KeBugCheck(MEMORY_MANAGEMENT);
3235 }
3236
3237 AdjustOffset = PAGE_ROUND_DOWN(FileOffset.u.LowPart);
3238 OffsetAdjustment = FileOffset.u.LowPart - AdjustOffset;
3239 FileOffset.u.LowPart = AdjustOffset;
3240
3241 BufferSize = Length + OffsetAdjustment;
3242 BufferSize = PAGE_ROUND_UP(BufferSize);
3243
3244 /* Flush data since we're about to perform a non-cached read */
3245 KeEnterCriticalRegion();
3246 CcFlushCache(FileObject->SectionObjectPointer,
3247 &FileOffset,
3248 BufferSize,
3249 &Iosb);
3250 KeLeaveCriticalRegion();
3251
3252 /*
3253 * It's ok to use paged pool, because this is a temporary buffer only used in
3254 * the loading of executables. The assumption is that MmCreateSection is
3255 * always called at low IRQLs and that these buffers don't survive a brief
3256 * initialization phase
3257 */
3258 Buffer = ExAllocatePoolWithTag(PagedPool,
3259 BufferSize,
3260 'rXmM');
3261 if (!Buffer)
3262 {
3263 return STATUS_INSUFFICIENT_RESOURCES;
3264 }
3265
3266 UsedSize = 0;
3267
3268 Status = MiSimpleRead(FileObject, &FileOffset, Buffer, BufferSize, TRUE, &Iosb);
3269
3270 UsedSize = (ULONG)Iosb.Information;
3271
3272 if(NT_SUCCESS(Status) && UsedSize < OffsetAdjustment)
3273 {
3274 Status = STATUS_IN_PAGE_ERROR;
3275 ASSERT(!NT_SUCCESS(Status));
3276 }
3277
3278 if(NT_SUCCESS(Status))
3279 {
3280 *Data = (PVOID)((ULONG_PTR)Buffer + OffsetAdjustment);
3281 *AllocBase = Buffer;
3282 *ReadSize = UsedSize - OffsetAdjustment;
3283 }
3284 else
3285 {
3286 ExFreePoolWithTag(Buffer, 'rXmM');
3287 }
3288
3289 return Status;
3290 }
3291
3292 #ifdef NASSERT
3293 # define MmspAssertSegmentsSorted(OBJ_) ((void)0)
3294 # define MmspAssertSegmentsNoOverlap(OBJ_) ((void)0)
3295 # define MmspAssertSegmentsPageAligned(OBJ_) ((void)0)
3296 #else
3297 static
3298 VOID
3299 NTAPI
3300 MmspAssertSegmentsSorted(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3301 {
3302 ULONG i;
3303
3304 for( i = 1; i < ImageSectionObject->NrSegments; ++ i )
3305 {
3306 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3307 ImageSectionObject->Segments[i - 1].Image.VirtualAddress);
3308 }
3309 }
3310
3311 static
3312 VOID
3313 NTAPI
3314 MmspAssertSegmentsNoOverlap(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3315 {
3316 ULONG i;
3317
3318 MmspAssertSegmentsSorted(ImageSectionObject);
3319
3320 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3321 {
3322 ASSERT(ImageSectionObject->Segments[i].Length.QuadPart > 0);
3323
3324 if(i > 0)
3325 {
3326 ASSERT(ImageSectionObject->Segments[i].Image.VirtualAddress >=
3327 (ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
3328 ImageSectionObject->Segments[i - 1].Length.QuadPart));
3329 }
3330 }
3331 }
3332
3333 static
3334 VOID
3335 NTAPI
3336 MmspAssertSegmentsPageAligned(IN PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3337 {
3338 ULONG i;
3339
3340 for( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3341 {
3342 ASSERT((ImageSectionObject->Segments[i].Image.VirtualAddress % PAGE_SIZE) == 0);
3343 ASSERT((ImageSectionObject->Segments[i].Length.QuadPart % PAGE_SIZE) == 0);
3344 }
3345 }
3346 #endif
3347
3348 static
3349 int
3350 __cdecl
3351 MmspCompareSegments(const void * x,
3352 const void * y)
3353 {
3354 const MM_SECTION_SEGMENT *Segment1 = (const MM_SECTION_SEGMENT *)x;
3355 const MM_SECTION_SEGMENT *Segment2 = (const MM_SECTION_SEGMENT *)y;
3356
3357 return
3358 (Segment1->Image.VirtualAddress - Segment2->Image.VirtualAddress) >>
3359 ((sizeof(ULONG_PTR) - sizeof(int)) * 8);
3360 }
3361
3362 /*
3363 * Ensures an image section's segments are sorted in memory
3364 */
3365 static
3366 VOID
3367 NTAPI
3368 MmspSortSegments(IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3369 IN ULONG Flags)
3370 {
3371 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_SORTED)
3372 {
3373 MmspAssertSegmentsSorted(ImageSectionObject);
3374 }
3375 else
3376 {
3377 qsort(ImageSectionObject->Segments,
3378 ImageSectionObject->NrSegments,
3379 sizeof(ImageSectionObject->Segments[0]),
3380 MmspCompareSegments);
3381 }
3382 }
3383
3384
3385 /*
3386 * Ensures an image section's segments don't overlap in memory and don't have
3387 * gaps and don't have a null size. We let them map to overlapping file regions,
3388 * though - that's not necessarily an error
3389 */
3390 static
3391 BOOLEAN
3392 NTAPI
3393 MmspCheckSegmentBounds
3394 (
3395 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3396 IN ULONG Flags
3397 )
3398 {
3399 ULONG i;
3400
3401 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_NO_OVERLAP)
3402 {
3403 MmspAssertSegmentsNoOverlap(ImageSectionObject);
3404 return TRUE;
3405 }
3406
3407 ASSERT(ImageSectionObject->NrSegments >= 1);
3408
3409 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3410 {
3411 if(ImageSectionObject->Segments[i].Length.QuadPart == 0)
3412 {
3413 return FALSE;
3414 }
3415
3416 if(i > 0)
3417 {
3418 /*
3419 * TODO: relax the limitation on gaps. For example, gaps smaller than a
3420 * page could be OK (Windows seems to be OK with them), and larger gaps
3421 * could lead to image sections spanning several discontiguous regions
3422 * (NtMapViewOfSection could then refuse to map them, and they could
3423 * e.g. only be allowed as parameters to NtCreateProcess, like on UNIX)
3424 */
3425 if ((ImageSectionObject->Segments[i - 1].Image.VirtualAddress +
3426 ImageSectionObject->Segments[i - 1].Length.QuadPart) !=
3427 ImageSectionObject->Segments[i].Image.VirtualAddress)
3428 {
3429 return FALSE;
3430 }
3431 }
3432 }
3433
3434 return TRUE;
3435 }
3436
3437 /*
3438 * Merges and pads an image section's segments until they all are page-aligned
3439 * and have a size that is a multiple of the page size
3440 */
3441 static
3442 BOOLEAN
3443 NTAPI
3444 MmspPageAlignSegments
3445 (
3446 IN OUT PMM_IMAGE_SECTION_OBJECT ImageSectionObject,
3447 IN ULONG Flags
3448 )
3449 {
3450 ULONG i;
3451 ULONG LastSegment;
3452 PMM_SECTION_SEGMENT EffectiveSegment;
3453
3454 if (Flags & EXEFMT_LOAD_ASSUME_SEGMENTS_PAGE_ALIGNED)
3455 {
3456 MmspAssertSegmentsPageAligned(ImageSectionObject);
3457 return TRUE;
3458 }
3459
3460 LastSegment = 0;
3461 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3462
3463 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3464 {
3465 /*
3466 * The first segment requires special handling
3467 */
3468 if (i == 0)
3469 {
3470 ULONG_PTR VirtualAddress;
3471 ULONG_PTR VirtualOffset;
3472
3473 VirtualAddress = EffectiveSegment->Image.VirtualAddress;
3474
3475 /* Round down the virtual address to the nearest page */
3476 EffectiveSegment->Image.VirtualAddress = PAGE_ROUND_DOWN(VirtualAddress);
3477
3478 /* Round up the virtual size to the nearest page */
3479 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(VirtualAddress + EffectiveSegment->Length.QuadPart) -
3480 EffectiveSegment->Image.VirtualAddress;
3481
3482 /* Adjust the raw address and size */
3483 VirtualOffset = VirtualAddress - EffectiveSegment->Image.VirtualAddress;
3484
3485 if (EffectiveSegment->Image.FileOffset < VirtualOffset)
3486 {
3487 return FALSE;
3488 }
3489
3490 /*
3491 * Garbage in, garbage out: unaligned base addresses make the file
3492 * offset point in curious and odd places, but that's what we were
3493 * asked for
3494 */
3495 EffectiveSegment->Image.FileOffset -= VirtualOffset;
3496 EffectiveSegment->RawLength.QuadPart += VirtualOffset;
3497 }
3498 else
3499 {
3500 PMM_SECTION_SEGMENT Segment = &ImageSectionObject->Segments[i];
3501 ULONG_PTR EndOfEffectiveSegment;
3502
3503 EndOfEffectiveSegment = (ULONG_PTR)(EffectiveSegment->Image.VirtualAddress + EffectiveSegment->Length.QuadPart);
3504 ASSERT((EndOfEffectiveSegment % PAGE_SIZE) == 0);
3505
3506 /*
3507 * The current segment begins exactly where the current effective
3508 * segment ended, therefore beginning a new effective segment
3509 */
3510 if (EndOfEffectiveSegment == Segment->Image.VirtualAddress)
3511 {
3512 LastSegment ++;
3513 ASSERT(LastSegment <= i);
3514 ASSERT(LastSegment < ImageSectionObject->NrSegments);
3515
3516 EffectiveSegment = &ImageSectionObject->Segments[LastSegment];
3517
3518 if (LastSegment != i)
3519 {
3520 /*
3521 * Copy the current segment. If necessary, the effective segment
3522 * will be expanded later
3523 */
3524 *EffectiveSegment = *Segment;
3525 }
3526
3527 /*
3528 * Page-align the virtual size. We know for sure the virtual address
3529 * already is
3530 */
3531 ASSERT((EffectiveSegment->Image.VirtualAddress % PAGE_SIZE) == 0);
3532 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(EffectiveSegment->Length.QuadPart);
3533 }
3534 /*
3535 * The current segment is still part of the current effective segment:
3536 * extend the effective segment to reflect this
3537 */
3538 else if (EndOfEffectiveSegment > Segment->Image.VirtualAddress)
3539 {
3540 static const ULONG FlagsToProtection[16] =
3541 {
3542 PAGE_NOACCESS,
3543 PAGE_READONLY,
3544 PAGE_READWRITE,
3545 PAGE_READWRITE,
3546 PAGE_EXECUTE_READ,
3547 PAGE_EXECUTE_READ,
3548 PAGE_EXECUTE_READWRITE,
3549 PAGE_EXECUTE_READWRITE,
3550 PAGE_WRITECOPY,
3551 PAGE_WRITECOPY,
3552 PAGE_WRITECOPY,
3553 PAGE_WRITECOPY,
3554 PAGE_EXECUTE_WRITECOPY,
3555 PAGE_EXECUTE_WRITECOPY,
3556 PAGE_EXECUTE_WRITECOPY,
3557 PAGE_EXECUTE_WRITECOPY
3558 };
3559
3560 unsigned ProtectionFlags;
3561
3562 /*
3563 * Extend the file size
3564 */
3565
3566 /* Unaligned segments must be contiguous within the file */
3567 if (Segment->Image.FileOffset != (EffectiveSegment->Image.FileOffset +
3568 EffectiveSegment->RawLength.QuadPart))
3569 {
3570 return FALSE;
3571 }
3572
3573 EffectiveSegment->RawLength.QuadPart += Segment->RawLength.QuadPart;
3574
3575 /*
3576 * Extend the virtual size
3577 */
3578 ASSERT(PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) >= EndOfEffectiveSegment);
3579
3580 EffectiveSegment->Length.QuadPart = PAGE_ROUND_UP(Segment->Image.VirtualAddress + Segment->Length.QuadPart) -
3581 EffectiveSegment->Image.VirtualAddress;
3582
3583 /*
3584 * Merge the protection
3585 */
3586 EffectiveSegment->Protection |= Segment->Protection;
3587
3588 /* Clean up redundance */
3589 ProtectionFlags = 0;
3590
3591 if(EffectiveSegment->Protection & PAGE_IS_READABLE)
3592 ProtectionFlags |= 1 << 0;
3593
3594 if(EffectiveSegment->Protection & PAGE_IS_WRITABLE)
3595 ProtectionFlags |= 1 << 1;
3596
3597 if(EffectiveSegment->Protection & PAGE_IS_EXECUTABLE)
3598 ProtectionFlags |= 1 << 2;
3599
3600 if(EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3601 ProtectionFlags |= 1 << 3;
3602
3603 ASSERT(ProtectionFlags < 16);
3604 EffectiveSegment->Protection = FlagsToProtection[ProtectionFlags];
3605
3606 /* If a segment was required to be shared and cannot, fail */
3607 if(!(Segment->Protection & PAGE_IS_WRITECOPY) &&
3608 EffectiveSegment->Protection & PAGE_IS_WRITECOPY)
3609 {
3610 return FALSE;
3611 }
3612 }
3613 /*
3614 * We assume no holes between segments at this point
3615 */
3616 else
3617 {
3618 KeBugCheck(MEMORY_MANAGEMENT);
3619 }
3620 }
3621 }
3622 ImageSectionObject->NrSegments = LastSegment + 1;
3623
3624 return TRUE;
3625 }
3626
3627 NTSTATUS
3628 ExeFmtpCreateImageSection(PFILE_OBJECT FileObject,
3629 PMM_IMAGE_SECTION_OBJECT ImageSectionObject)
3630 {
3631 LARGE_INTEGER Offset;
3632 PVOID FileHeader;
3633 PVOID FileHeaderBuffer;
3634 ULONG FileHeaderSize;
3635 ULONG Flags;
3636 ULONG OldNrSegments;
3637 NTSTATUS Status;
3638 ULONG i;
3639
3640 /*
3641 * Read the beginning of the file (2 pages). Should be enough to contain
3642 * all (or most) of the headers
3643 */
3644 Offset.QuadPart = 0;
3645
3646 Status = ExeFmtpReadFile (FileObject,
3647 &Offset,
3648 PAGE_SIZE * 2,
3649 &FileHeader,
3650 &FileHeaderBuffer,
3651 &FileHeaderSize);
3652
3653 if (!NT_SUCCESS(Status))
3654 return Status;
3655
3656 if (FileHeaderSize == 0)
3657 {
3658 ExFreePool(FileHeaderBuffer);
3659 return STATUS_UNSUCCESSFUL;
3660 }
3661
3662 /*
3663 * Look for a loader that can handle this executable
3664 */
3665 for (i = 0; i < RTL_NUMBER_OF(ExeFmtpLoaders); ++ i)
3666 {
3667 RtlZeroMemory(ImageSectionObject, sizeof(*ImageSectionObject));
3668 Flags = 0;
3669
3670 Status = ExeFmtpLoaders[i](FileHeader,
3671 FileHeaderSize,
3672 FileObject,
3673 ImageSectionObject,
3674 &Flags,
3675 ExeFmtpReadFile,
3676 ExeFmtpAllocateSegments);
3677
3678 if (!NT_SUCCESS(Status))
3679 {
3680 if (ImageSectionObject->Segments)
3681 {
3682 ExFreePool(ImageSectionObject->Segments);
3683 ImageSectionObject->Segments = NULL;
3684 }
3685 }
3686
3687 if (Status != STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3688 break;
3689 }
3690
3691 ExFreePoolWithTag(FileHeaderBuffer, 'rXmM');
3692
3693 /*
3694 * No loader handled the format
3695 */
3696 if (Status == STATUS_ROS_EXEFMT_UNKNOWN_FORMAT)
3697 {
3698 Status = STATUS_INVALID_IMAGE_NOT_MZ;
3699 ASSERT(!NT_SUCCESS(Status));
3700 }
3701
3702 if (!NT_SUCCESS(Status))
3703 return Status;
3704
3705 ASSERT(ImageSectionObject->Segments != NULL);
3706
3707 /*
3708 * Some defaults
3709 */
3710 /* FIXME? are these values platform-dependent? */
3711 if (ImageSectionObject->ImageInformation.MaximumStackSize == 0)
3712 ImageSectionObject->ImageInformation.MaximumStackSize = 0x40000;
3713
3714 if(ImageSectionObject->ImageInformation.CommittedStackSize == 0)
3715 ImageSectionObject->ImageInformation.CommittedStackSize = 0x1000;
3716
3717 if(ImageSectionObject->BasedAddress == NULL)
3718 {
3719 if(ImageSectionObject->ImageInformation.ImageCharacteristics & IMAGE_FILE_DLL)
3720 ImageSectionObject->BasedAddress = (PVOID)0x10000000;
3721 else
3722 ImageSectionObject->BasedAddress = (PVOID)0x00400000;
3723 }
3724
3725 /*
3726 * And now the fun part: fixing the segments
3727 */
3728
3729 /* Sort them by virtual address */
3730 MmspSortSegments(ImageSectionObject, Flags);
3731
3732 /* Ensure they don't overlap in memory */
3733 if (!MmspCheckSegmentBounds(ImageSectionObject, Flags))
3734 return STATUS_INVALID_IMAGE_FORMAT;
3735
3736 /* Ensure they are aligned */
3737 OldNrSegments = ImageSectionObject->NrSegments;
3738
3739 if (!MmspPageAlignSegments(ImageSectionObject, Flags))
3740 return STATUS_INVALID_IMAGE_FORMAT;
3741
3742 /* Trim them if the alignment phase merged some of them */
3743 if (ImageSectionObject->NrSegments < OldNrSegments)
3744 {
3745 PMM_SECTION_SEGMENT Segments;
3746 SIZE_T SizeOfSegments;
3747
3748 SizeOfSegments = sizeof(MM_SECTION_SEGMENT) * ImageSectionObject->NrSegments;
3749
3750 Segments = ExAllocatePoolWithTag(PagedPool,
3751 SizeOfSegments,
3752 TAG_MM_SECTION_SEGMENT);
3753
3754 if (Segments == NULL)
3755 return STATUS_INSUFFICIENT_RESOURCES;
3756
3757 RtlCopyMemory(Segments, ImageSectionObject->Segments, SizeOfSegments);
3758 ExFreePool(ImageSectionObject->Segments);
3759 ImageSectionObject->Segments = Segments;
3760 }
3761
3762 /* And finish their initialization */
3763 for ( i = 0; i < ImageSectionObject->NrSegments; ++ i )
3764 {
3765 ExInitializeFastMutex(&ImageSectionObject->Segments[i].Lock);
3766 ImageSectionObject->Segments[i].ReferenceCount = 1;
3767 MiInitializeSectionPageTable(&ImageSectionObject->Segments[i]);
3768 }
3769
3770 ASSERT(NT_SUCCESS(Status));
3771 return Status;
3772 }
3773
3774 NTSTATUS
3775 MmCreateImageSection(PROS_SECTION_OBJECT *SectionObject,
3776 ACCESS_MASK DesiredAccess,
3777 POBJECT_ATTRIBUTES ObjectAttributes,
3778 PLARGE_INTEGER UMaximumSize,
3779 ULONG SectionPageProtection,
3780 ULONG AllocationAttributes,
3781 PFILE_OBJECT FileObject)
3782 {
3783 PROS_SECTION_OBJECT Section;
3784 NTSTATUS Status;
3785 PMM_SECTION_SEGMENT SectionSegments;
3786 PMM_IMAGE_SECTION_OBJECT ImageSectionObject;
3787 ULONG i;
3788
3789 if (FileObject == NULL)
3790 return STATUS_INVALID_FILE_FOR_SECTION;
3791
3792 #ifndef NEWCC
3793 if (FileObject->SectionObjectPointer->SharedCacheMap == NULL)
3794 {
3795 DPRINT1("Denying section creation due to missing cache initialization\n");
3796 return STATUS_INVALID_FILE_FOR_SECTION;
3797 }
3798 #endif
3799
3800 /*
3801 * Create the section
3802 */
3803 Status = ObCreateObject (ExGetPreviousMode(),
3804 MmSectionObjectType,
3805 ObjectAttributes,
3806 ExGetPreviousMode(),
3807 NULL,
3808 sizeof(ROS_SECTION_OBJECT),
3809 0,
3810 0,
3811 (PVOID*)(PVOID)&Section);
3812 if (!NT_SUCCESS(Status))
3813 {
3814 ObDereferenceObject(FileObject);
3815 return(Status);
3816 }
3817
3818 /*
3819 * Initialize it
3820 */
3821 RtlZeroMemory(Section, sizeof(ROS_SECTION_OBJECT));
3822 Section->Type = 'SC';
3823 Section->Size = 'TN';
3824 Section->SectionPageProtection = SectionPageProtection;
3825 Section->AllocationAttributes = AllocationAttributes;
3826
3827 #ifndef NEWCC
3828 /*
3829 * Initialized caching for this file object if previously caching
3830 * was initialized for the same on disk file
3831 */
3832 Status = CcTryToInitializeFileCache(FileObject);
3833 #else
3834 Status = STATUS_SUCCESS;
3835 #endif
3836
3837 if (!NT_SUCCESS(Status) || FileObject->SectionObjectPointer->ImageSectionObject == NULL)